1c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright/* 2c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Copyright (C) 2014 The Android Open Source Project 3c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * 4c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Licensed under the Apache License, Version 2.0 (the "License"); 5c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * you may not use this file except in compliance with the License. 6c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * You may obtain a copy of the License at 7c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * 8c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * http://www.apache.org/licenses/LICENSE-2.0 9c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * 10c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Unless required by applicable law or agreed to in writing, software 11c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * distributed under the License is distributed on an "AS IS" BASIS, 12c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * See the License for the specific language governing permissions and 14c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * limitations under the License. 15c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright */ 16c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 17c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightpackage com.android.server.media.projection; 18c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 19c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport com.android.server.Watchdog; 20c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 21c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.Manifest; 22c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.app.AppOpsManager; 23c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.Context; 24c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.pm.PackageManager; 25c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.hardware.display.DisplayManager; 2689c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wrightimport android.media.MediaRouter; 27c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.media.projection.IMediaProjectionManager; 28c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.media.projection.IMediaProjection; 29c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.media.projection.IMediaProjectionCallback; 30d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wrightimport android.media.projection.IMediaProjectionWatcherCallback; 31d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wrightimport android.media.projection.MediaProjectionInfo; 32c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.media.projection.MediaProjectionManager; 33c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.Binder; 34c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.Handler; 35c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.IBinder; 36c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.Looper; 37c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.RemoteException; 38d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wrightimport android.os.UserHandle; 39c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.util.ArrayMap; 40c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.util.Slog; 41c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 42fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils; 43c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport com.android.server.SystemService; 44c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 45c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport java.io.FileDescriptor; 46c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport java.io.PrintWriter; 47c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport java.util.Map; 48c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 49c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright/** 50c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Manages MediaProjection sessions. 51c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * 52c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * The {@link MediaProjectionManagerService} manages the creation and lifetime of MediaProjections, 53c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * as well as the capabilities they grant. Any service using MediaProjection tokens as permission 54c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * grants <b>must</b> validate the token before use by calling {@link 55c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * IMediaProjectionService#isValidMediaProjection}. 56c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright */ 57c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightpublic final class MediaProjectionManagerService extends SystemService 58c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright implements Watchdog.Monitor { 59c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private static final String TAG = "MediaProjectionManagerService"; 60c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 61c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private final Object mLock = new Object(); // Protects the list of media projections 62d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters; 63d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private final CallbackDelegate mCallbackDelegate; 64c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 65c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private final Context mContext; 66c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private final AppOpsManager mAppOps; 67c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 6889c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright private final MediaRouter mMediaRouter; 6989c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright private final MediaRouterCallback mMediaRouterCallback; 7089c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright private MediaRouter.RouteInfo mMediaRouteInfo; 7189c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright 72d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private IBinder mProjectionToken; 73d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private MediaProjection mProjectionGrant; 74d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 75c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public MediaProjectionManagerService(Context context) { 76c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright super(context); 77c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mContext = context; 78d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>(); 79d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallbackDelegate = new CallbackDelegate(); 80c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 8189c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE); 8289c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouterCallback = new MediaRouterCallback(); 83c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Watchdog.getInstance().addMonitor(this); 84c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 85c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 86c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override 87c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void onStart() { 88c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright publishBinderService(Context.MEDIA_PROJECTION_SERVICE, new BinderService(), 89c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright false /*allowIsolated*/); 904444c5b27874866f18cd8f4abb8914cc17857ea7Jason Monk mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback, 914444c5b27874866f18cd8f4abb8914cc17857ea7Jason Monk MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); 9289c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 9389c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright 9489c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright @Override 9589c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright public void onSwitchUser(int userId) { 9689c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouter.rebindAsUser(userId); 9705aab5810a5f0296c3650f104c58479b2765c0b7Michael Wright synchronized (mLock) { 9805aab5810a5f0296c3650f104c58479b2765c0b7Michael Wright if (mProjectionGrant != null) { 9905aab5810a5f0296c3650f104c58479b2765c0b7Michael Wright mProjectionGrant.stop(); 10005aab5810a5f0296c3650f104c58479b2765c0b7Michael Wright } 10105aab5810a5f0296c3650f104c58479b2765c0b7Michael Wright } 102c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 103c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 104c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override 105c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void monitor() { 106c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { /* check for deadlock */ } 107c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 108c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 109d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void startProjectionLocked(final MediaProjection projection) { 110d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mProjectionGrant != null) { 111d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionGrant.stop(); 112d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 11389c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright if (mMediaRouteInfo != null) { 11489c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouter.getDefaultRoute().select(); 11589c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 116d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionToken = projection.asBinder(); 117d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionGrant = projection; 118d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright dispatchStart(projection); 119d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 120d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 121d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void stopProjectionLocked(final MediaProjection projection) { 122d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionToken = null; 123d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionGrant = null; 124d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright dispatchStop(projection); 125d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 126d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 127d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void addCallback(final IMediaProjectionWatcherCallback callback) { 128d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { 129d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 130d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void binderDied() { 131ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo removeCallback(callback); 132d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 133d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright }; 134d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 135d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallbackDelegate.add(callback); 136d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright linkDeathRecipientLocked(callback, deathRecipient); 137d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 138d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 139d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 140d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void removeCallback(IMediaProjectionWatcherCallback callback) { 141d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 142d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright unlinkDeathRecipientLocked(callback); 14378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mCallbackDelegate.remove(callback); 144d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 145d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 146d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 147d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void linkDeathRecipientLocked(IMediaProjectionWatcherCallback callback, 148d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright IBinder.DeathRecipient deathRecipient) { 149d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 150d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final IBinder token = callback.asBinder(); 151d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright token.linkToDeath(deathRecipient, 0); 152d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mDeathEaters.put(token, deathRecipient); 153d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } catch (RemoteException e) { 154d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.e(TAG, "Unable to link to death for media projection monitoring callback", e); 155d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 156d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 157d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 158d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void unlinkDeathRecipientLocked(IMediaProjectionWatcherCallback callback) { 159d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final IBinder token = callback.asBinder(); 160d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright IBinder.DeathRecipient deathRecipient = mDeathEaters.remove(token); 161d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (deathRecipient != null) { 162d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright token.unlinkToDeath(deathRecipient, 0); 163d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 164d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 165d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 166d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void dispatchStart(MediaProjection projection) { 167d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallbackDelegate.dispatchStart(projection); 168d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 169d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 170d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private void dispatchStop(MediaProjection projection) { 171d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallbackDelegate.dispatchStop(projection); 172d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 173d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 174d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private boolean isValidMediaProjection(IBinder token) { 175d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 176d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mProjectionToken != null) { 177d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return mProjectionToken.equals(token); 178d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 179d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return false; 180d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 181d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 182d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 183d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private MediaProjectionInfo getActiveProjectionInfo() { 184d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 185d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mProjectionGrant == null) { 186d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return null; 187d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 188d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return mProjectionGrant.getProjectionInfo(); 189d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 190d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 191d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 192c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private void dump(final PrintWriter pw) { 193c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)"); 194c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 195d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright pw.println("Media Projection: "); 196d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mProjectionGrant != null ) { 197d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionGrant.dump(pw); 198d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } else { 199d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright pw.println("null"); 200c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 201c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 202c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 203c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 204c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private final class BinderService extends IMediaProjectionManager.Stub { 205c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 206c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 207c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public boolean hasProjectionPermission(int uid, String packageName) { 208c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright long token = Binder.clearCallingIdentity(); 209c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright boolean hasPermission = false; 210c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright try { 211c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright hasPermission |= checkPermission(packageName, 212c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) 2136720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright || mAppOps.noteOpNoThrow( 214c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright AppOpsManager.OP_PROJECT_MEDIA, uid, packageName) 215c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright == AppOpsManager.MODE_ALLOWED; 216c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } finally { 217c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Binder.restoreCallingIdentity(token); 218c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 219c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return hasPermission; 220c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 221c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 222c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 223c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public IMediaProjection createProjection(int uid, String packageName, int type, 224c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright boolean isPermanentGrant) { 225d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) 226c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright != PackageManager.PERMISSION_GRANTED) { 227d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant " 228c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright + "projection permission"); 229c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 230d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (packageName == null || packageName.isEmpty()) { 231d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new IllegalArgumentException("package name must not be empty"); 232d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 233c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright long callingToken = Binder.clearCallingIdentity(); 234c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright MediaProjection projection; 235c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright try { 236c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright projection = new MediaProjection(type, uid, packageName); 237c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright if (isPermanentGrant) { 238c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA, 239c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED); 240c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 241c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } finally { 242c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Binder.restoreCallingIdentity(callingToken); 243c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 244c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return projection; 245c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 246c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 247c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 248c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public boolean isValidMediaProjection(IMediaProjection projection) { 249d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return MediaProjectionManagerService.this.isValidMediaProjection( 250d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright projection.asBinder()); 251d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 252d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 253d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override // Binder call 254d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public MediaProjectionInfo getActiveProjectionInfo() { 255d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) 256d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright != PackageManager.PERMISSION_GRANTED) { 257d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " 258d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + "projection callbacks"); 259d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 260d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final long token = Binder.clearCallingIdentity(); 261d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 262d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return MediaProjectionManagerService.this.getActiveProjectionInfo(); 263d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } finally { 264d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Binder.restoreCallingIdentity(token); 265d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 266d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 267d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 268d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override // Binder call 269d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void stopActiveProjection() { 270d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) 271d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright != PackageManager.PERMISSION_GRANTED) { 272d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " 273d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + "projection callbacks"); 274d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 275d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final long token = Binder.clearCallingIdentity(); 276d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 277d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mProjectionGrant != null) { 278d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mProjectionGrant.stop(); 279d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 280d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } finally { 281d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Binder.restoreCallingIdentity(token); 282d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 283d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 284d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 285d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 286d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override //Binder call 287d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void addCallback(final IMediaProjectionWatcherCallback callback) { 288d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) 289d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright != PackageManager.PERMISSION_GRANTED) { 290d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " 291d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + "projection callbacks"); 292d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 293d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final long token = Binder.clearCallingIdentity(); 294d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 295d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright MediaProjectionManagerService.this.addCallback(callback); 296d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } finally { 297d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Binder.restoreCallingIdentity(token); 298d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 299d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 300d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 301d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 302d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void removeCallback(IMediaProjectionWatcherCallback callback) { 303d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) 304d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright != PackageManager.PERMISSION_GRANTED) { 305d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to remove " 306d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + "projection callbacks"); 307d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 308d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright final long token = Binder.clearCallingIdentity(); 309d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 310d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright MediaProjectionManagerService.this.removeCallback(callback); 311d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } finally { 312d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Binder.restoreCallingIdentity(token); 313d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 314c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 315c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 316c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 317c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 318fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 319c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright final long token = Binder.clearCallingIdentity(); 320c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright try { 3219b84309bea122e63009408b3fa94a3e498ae60c9John Spurlock MediaProjectionManagerService.this.dump(pw); 322c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } finally { 323c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Binder.restoreCallingIdentity(token); 324c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 325c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 326c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 327d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 328c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private boolean checkPermission(String packageName, String permission) { 329c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return mContext.getPackageManager().checkPermission(permission, packageName) 330c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright == PackageManager.PERMISSION_GRANTED; 331c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 332c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 333c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 334d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private final class MediaProjection extends IMediaProjection.Stub { 335d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public final int uid; 336d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public final String packageName; 337d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public final UserHandle userHandle; 338c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 339ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo private IMediaProjectionCallback mCallback; 340c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private IBinder mToken; 341d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private IBinder.DeathRecipient mDeathEater; 342c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private int mType; 343c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 344c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public MediaProjection(int type, int uid, String packageName) { 345c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mType = type; 346c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright this.uid = uid; 347c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright this.packageName = packageName; 348d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright userHandle = new UserHandle(UserHandle.getUserId(uid)); 349c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 350c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 351c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 352c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public boolean canProjectVideo() { 353c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return mType == MediaProjectionManager.TYPE_MIRRORING || 354c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE; 355c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 356c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 357c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 358c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public boolean canProjectSecureVideo() { 359c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return false; 360c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 361c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 362c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 363c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public boolean canProjectAudio() { 364c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return mType == MediaProjectionManager.TYPE_MIRRORING || 365c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mType == MediaProjectionManager.TYPE_PRESENTATION; 366c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 367c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 368c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 3696720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright public int applyVirtualDisplayFlags(int flags) { 3706720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) { 3716720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; 3726720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR 3736720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 3746720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright return flags; 3756720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright } else if (mType == MediaProjectionManager.TYPE_MIRRORING) { 3766720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags &= ~(DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | 3776720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR); 3786720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | 3796720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 3806720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright return flags; 3816720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright } else if (mType == MediaProjectionManager.TYPE_PRESENTATION) { 3826720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; 3836720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | 3846720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION | 3856720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; 3866720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright return flags; 3876720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright } else { 3886720be4e8c65e90d4453ddad5cef192bc3820038Michael Wright throw new RuntimeException("Unknown MediaProjection type"); 389c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 390c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 391c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 392c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 393d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void start(final IMediaProjectionCallback callback) { 394c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright if (callback == null) { 395c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright throw new IllegalArgumentException("callback must not be null"); 396c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 397c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 398d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (isValidMediaProjection(asBinder())) { 399c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright throw new IllegalStateException( 400c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright "Cannot start already started MediaProjection"); 401c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 402ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo mCallback = callback; 403ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo registerCallback(mCallback); 404c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright try { 405c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mToken = callback.asBinder(); 406d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mDeathEater = new IBinder.DeathRecipient() { 407d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 408d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void binderDied() { 409d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallbackDelegate.remove(callback); 410d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright stop(); 411d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 412d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright }; 413d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mToken.linkToDeath(mDeathEater, 0); 414c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } catch (RemoteException e) { 415c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Slog.w(TAG, 416c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright "MediaProjectionCallbacks must be valid, aborting MediaProjection", e); 417c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return; 418c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 419d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright startProjectionLocked(this); 420c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 421c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 422c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 423c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override // Binder call 424c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void stop() { 425c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 426d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (!isValidMediaProjection(asBinder())) { 427c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright Slog.w(TAG, "Attempted to stop inactive MediaProjection " 428c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright + "(uid=" + Binder.getCallingUid() + ", " 429c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright + "pid=" + Binder.getCallingPid() + ")"); 430c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return; 431c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 432d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright stopProjectionLocked(this); 433ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo mToken.unlinkToDeath(mDeathEater, 0); 434ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo mToken = null; 435ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo unregisterCallback(mCallback); 436ac3f8e5534b1653a29714b63a3afbc6c1a90e669Jae Seo mCallback = null; 437c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 438c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 439c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 440c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override 441cde5bb45cc86d181d96ee69da1832e6132162871Michael Wright public void registerCallback(IMediaProjectionCallback callback) { 442c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright if (callback == null) { 443c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright throw new IllegalArgumentException("callback must not be null"); 444c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 445c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mCallbackDelegate.add(callback); 446c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 447c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 448c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright @Override 449cde5bb45cc86d181d96ee69da1832e6132162871Michael Wright public void unregisterCallback(IMediaProjectionCallback callback) { 450c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright if (callback == null) { 451c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright throw new IllegalArgumentException("callback must not be null"); 452c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 453c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mCallbackDelegate.remove(callback); 454c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 455c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 456d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public MediaProjectionInfo getProjectionInfo() { 457d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return new MediaProjectionInfo(packageName, userHandle); 458d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 459d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 460d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void dump(PrintWriter pw) { 461d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright pw.println("(" + packageName + ", uid=" + uid + "): " + typeToString(mType)); 462c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 463c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 464c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 46589c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright private class MediaRouterCallback extends MediaRouter.SimpleCallback { 46689c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright @Override 46789c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo info) { 46889c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright synchronized (mLock) { 46989c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright if ((type & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) { 47089c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouteInfo = info; 47189c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright if (mProjectionGrant != null) { 47289c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mProjectionGrant.stop(); 47389c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 47489c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 47589c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 47689c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 47789c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright 47889c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright @Override 47989c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright public void onRouteUnselected(MediaRouter route, int type, MediaRouter.RouteInfo info) { 48089c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright if (mMediaRouteInfo == info) { 48189c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright mMediaRouteInfo = null; 48289c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 48389c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 48489c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright } 48589c2cb6f31cddb926dea3498f8adf01a8cc1426aMichael Wright 486d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 487c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private static class CallbackDelegate { 488d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private Map<IBinder, IMediaProjectionCallback> mClientCallbacks; 489d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private Map<IBinder, IMediaProjectionWatcherCallback> mWatcherCallbacks; 490c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private Handler mHandler; 491c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private Object mLock = new Object(); 492c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 493c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public CallbackDelegate() { 494c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/); 495d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mClientCallbacks = new ArrayMap<IBinder, IMediaProjectionCallback>(); 496d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mWatcherCallbacks = new ArrayMap<IBinder, IMediaProjectionWatcherCallback>(); 497c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 498c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 499c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void add(IMediaProjectionCallback callback) { 500c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 501d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mClientCallbacks.put(callback.asBinder(), callback); 502d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 503d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 504d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 505d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void add(IMediaProjectionWatcherCallback callback) { 506d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 507d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mWatcherCallbacks.put(callback.asBinder(), callback); 508c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 509c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 510c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 511c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright public void remove(IMediaProjectionCallback callback) { 512c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 513d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mClientCallbacks.remove(callback.asBinder()); 514c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 515c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 516c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 517d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void remove(IMediaProjectionWatcherCallback callback) { 518c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright synchronized (mLock) { 519d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mWatcherCallbacks.remove(callback.asBinder()); 520d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 521d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 522d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 523d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void dispatchStart(MediaProjection projection) { 524d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (projection == null) { 525d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.e(TAG, "Tried to dispatch start notification for a null media projection." 526d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + " Ignoring!"); 527d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return; 528d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 529d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 530d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) { 531d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright MediaProjectionInfo info = projection.getProjectionInfo(); 532d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mHandler.post(new WatcherStartCallback(info, callback)); 533c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 534c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 535c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 536d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 537d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void dispatchStop(MediaProjection projection) { 538d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright if (projection == null) { 539d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.e(TAG, "Tried to dispatch stop notification for a null media projection." 540d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright + " Ignoring!"); 541d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright return; 542d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 543d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright synchronized (mLock) { 544d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright for (IMediaProjectionCallback callback : mClientCallbacks.values()) { 545d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mHandler.post(new ClientStopCallback(callback)); 546d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 547d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 548d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) { 549d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright MediaProjectionInfo info = projection.getProjectionInfo(); 550d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mHandler.post(new WatcherStopCallback(info, callback)); 551d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 552d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 553d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 554d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 555d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 556d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private static final class WatcherStartCallback implements Runnable { 557d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private IMediaProjectionWatcherCallback mCallback; 558d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private MediaProjectionInfo mInfo; 559d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 560d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public WatcherStartCallback(MediaProjectionInfo info, 561d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright IMediaProjectionWatcherCallback callback) { 562d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mInfo = info; 563d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback = callback; 564d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 565d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 566d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 567d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void run() { 568d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 569d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback.onStart(mInfo); 570d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } catch (RemoteException e) { 571d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.w(TAG, "Failed to notify media projection has stopped", e); 572d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 573d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 574d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 575d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 576d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private static final class WatcherStopCallback implements Runnable { 577d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private IMediaProjectionWatcherCallback mCallback; 578d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private MediaProjectionInfo mInfo; 579d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 580d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public WatcherStopCallback(MediaProjectionInfo info, 581d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright IMediaProjectionWatcherCallback callback) { 582d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mInfo = info; 583d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback = callback; 584d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 585d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 586d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 587d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void run() { 588d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 589d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback.onStop(mInfo); 590d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } catch (RemoteException e) { 591d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.w(TAG, "Failed to notify media projection has stopped", e); 592d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 593d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 594c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 595c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright 596d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private static final class ClientStopCallback implements Runnable { 597d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright private IMediaProjectionCallback mCallback; 598d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 599d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public ClientStopCallback(IMediaProjectionCallback callback) { 600d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback = callback; 601d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 602d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 603d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright @Override 604d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright public void run() { 605d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright try { 606d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright mCallback.onStop(); 607d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } catch (RemoteException e) { 608d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright Slog.w(TAG, "Failed to notify media projection has stopped", e); 609d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 610d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 611d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright } 612d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 613d86ecd26d78c7bcd7f820b9ef5e3f4c3eb037510Michael Wright 614c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright private static String typeToString(int type) { 615c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright switch (type) { 616c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright case MediaProjectionManager.TYPE_SCREEN_CAPTURE: 617c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return "TYPE_SCREEN_CAPTURE"; 618c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright case MediaProjectionManager.TYPE_MIRRORING: 619c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return "TYPE_MIRRORING"; 620c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright case MediaProjectionManager.TYPE_PRESENTATION: 621c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return "TYPE_PRESENTATION"; 622c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 623c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright return Integer.toString(type); 624c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright } 625c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright} 626