1/*
2** Copyright 2015, The Android Open Source Project
3**
4** Licensed under the Apache License, Version 2.0 (the "License");
5** you may not use this file except in compliance with the License.
6** You may obtain a copy of the License at
7**
8**     http://www.apache.org/licenses/LICENSE-2.0
9**
10** Unless required by applicable law or agreed to in writing, software
11** distributed under the License is distributed on an "AS IS" BASIS,
12** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13** See the License for the specific language governing permissions and
14** limitations under the License.
15*/
16
17package com.android.server.wm;
18
19import android.app.ActivityManager;
20import android.content.ClipData;
21import android.net.Uri;
22import android.os.Binder;
23import android.os.IBinder;
24import android.os.RemoteException;
25
26import com.android.internal.view.IDragAndDropPermissions;
27
28import java.util.ArrayList;
29
30class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
31        implements IBinder.DeathRecipient {
32
33    private final int mSourceUid;
34    private final String mTargetPackage;
35    private final int mMode;
36    private final int mSourceUserId;
37    private final int mTargetUserId;
38
39    private final ArrayList<Uri> mUris = new ArrayList<Uri>();
40
41    private IBinder mActivityToken = null;
42    private IBinder mPermissionOwnerToken = null;
43    private IBinder mTransientToken = null;
44
45    DragAndDropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode,
46                                  int sourceUserId, int targetUserId) {
47        mSourceUid = sourceUid;
48        mTargetPackage = targetPackage;
49        mMode = mode;
50        mSourceUserId = sourceUserId;
51        mTargetUserId = targetUserId;
52
53        clipData.collectUris(mUris);
54    }
55
56    @Override
57    public void take(IBinder activityToken) throws RemoteException {
58        if (mActivityToken != null || mPermissionOwnerToken != null) {
59            return;
60        }
61        mActivityToken = activityToken;
62
63        // Will throw if Activity is not found.
64        IBinder permissionOwner = ActivityManager.getService().
65                getUriPermissionOwnerForActivity(mActivityToken);
66
67        doTake(permissionOwner);
68    }
69
70    private void doTake(IBinder permissionOwner) throws RemoteException {
71        long origId = Binder.clearCallingIdentity();
72        try {
73            for (int i = 0; i < mUris.size(); i++) {
74                ActivityManager.getService().grantUriPermissionFromOwner(
75                        permissionOwner, mSourceUid, mTargetPackage, mUris.get(i), mMode,
76                        mSourceUserId, mTargetUserId);
77            }
78        } finally {
79            Binder.restoreCallingIdentity(origId);
80        }
81    }
82
83    @Override
84    public void takeTransient(IBinder transientToken) throws RemoteException {
85        if (mActivityToken != null || mPermissionOwnerToken != null) {
86            return;
87        }
88        mPermissionOwnerToken = ActivityManager.getService().newUriPermissionOwner("drop");
89        mTransientToken = transientToken;
90        mTransientToken.linkToDeath(this, 0);
91
92        doTake(mPermissionOwnerToken);
93    }
94
95    @Override
96    public void release() throws RemoteException {
97        if (mActivityToken == null && mPermissionOwnerToken == null) {
98            return;
99        }
100
101        IBinder permissionOwner = null;
102        if (mActivityToken != null) {
103            try {
104                permissionOwner = ActivityManager.getService().
105                        getUriPermissionOwnerForActivity(mActivityToken);
106            } catch (Exception e) {
107                // Activity is destroyed, permissions already revoked.
108                return;
109            } finally {
110                mActivityToken = null;
111            }
112        } else {
113            permissionOwner = mPermissionOwnerToken;
114            mPermissionOwnerToken = null;
115            mTransientToken.unlinkToDeath(this, 0);
116            mTransientToken = null;
117        }
118
119        for (int i = 0; i < mUris.size(); ++i) {
120            ActivityManager.getService().revokeUriPermissionFromOwner(
121                    permissionOwner, mUris.get(i), mMode, mSourceUserId);
122        }
123    }
124
125    @Override
126    public void binderDied() {
127        try {
128            release();
129        } catch (RemoteException e) {
130            // Cannot happen, local call.
131        }
132    }
133}
134