1 /*
2  * Copyright (C) 2018 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 
17 package com.android.server.uri;
18 
19 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
20 import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
21 
22 import android.os.Binder;
23 import android.os.IBinder;
24 import android.os.UserHandle;
25 import android.util.ArraySet;
26 import android.util.proto.ProtoOutputStream;
27 
28 import com.android.server.am.UriPermissionOwnerProto;
29 
30 import com.google.android.collect.Sets;
31 
32 import java.io.PrintWriter;
33 import java.util.ArrayList;
34 import java.util.Iterator;
35 import java.util.List;
36 
37 public class UriPermissionOwner {
38     private final UriGrantsManagerInternal mService;
39     private final Object mOwner;
40 
41     private Binder externalToken;
42 
43     private ArraySet<UriPermission> mReadPerms;
44     private ArraySet<UriPermission> mWritePerms;
45 
46     class ExternalToken extends Binder {
getOwner()47         UriPermissionOwner getOwner() {
48             return UriPermissionOwner.this;
49         }
50     }
51 
UriPermissionOwner(UriGrantsManagerInternal service, Object owner)52     public UriPermissionOwner(UriGrantsManagerInternal service, Object owner) {
53         mService = service;
54         mOwner = owner;
55     }
56 
getExternalToken()57     public Binder getExternalToken() {
58         if (externalToken == null) {
59             externalToken = new ExternalToken();
60         }
61         return externalToken;
62     }
63 
fromExternalToken(IBinder token)64     static UriPermissionOwner fromExternalToken(IBinder token) {
65         if (token instanceof ExternalToken) {
66             return ((ExternalToken) token).getOwner();
67         }
68         return null;
69     }
70 
removeUriPermissions()71     public void removeUriPermissions() {
72         removeUriPermissions(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
73     }
74 
removeUriPermissions(int mode)75     void removeUriPermissions(int mode) {
76         removeUriPermission(null, mode);
77     }
78 
removeUriPermission(GrantUri grantUri, int mode)79     void removeUriPermission(GrantUri grantUri, int mode) {
80         removeUriPermission(grantUri, mode, null, UserHandle.USER_ALL);
81     }
82 
removeUriPermission(GrantUri grantUri, int mode, String targetPgk, int targetUserId)83     void removeUriPermission(GrantUri grantUri, int mode, String targetPgk, int targetUserId) {
84         final List<UriPermission> permissionsToRemove = new ArrayList<>();
85 
86         synchronized (this) {
87             if ((mode & FLAG_GRANT_READ_URI_PERMISSION) != 0 && mReadPerms != null) {
88                 final Iterator<UriPermission> it = mReadPerms.iterator();
89                 while (it.hasNext()) {
90                     final UriPermission perm = it.next();
91                     if (grantUri != null && !grantUri.equals(perm.uri)) {
92                         continue;
93                     }
94                     if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) {
95                         continue;
96                     }
97                     if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) {
98                         continue;
99                     }
100                     permissionsToRemove.add(perm);
101                     perm.removeReadOwner(this);
102                     it.remove();
103                 }
104                 if (mReadPerms.isEmpty()) {
105                     mReadPerms = null;
106                 }
107             }
108 
109             if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0 && mWritePerms != null) {
110                 final Iterator<UriPermission> it = mWritePerms.iterator();
111                 while (it.hasNext()) {
112                     final UriPermission perm = it.next();
113                     if (grantUri != null && !grantUri.equals(perm.uri)) {
114                         continue;
115                     }
116                     if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) {
117                         continue;
118                     }
119                     if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) {
120                         continue;
121                     }
122                     permissionsToRemove.add(perm);
123                     perm.removeWriteOwner(this);
124                     it.remove();
125                 }
126                 if (mWritePerms.isEmpty()) {
127                     mWritePerms = null;
128                 }
129             }
130         }
131 
132         final int permissionsToRemoveSize = permissionsToRemove.size();
133         for (int i = 0; i < permissionsToRemoveSize; i++) {
134             mService.removeUriPermissionIfNeeded(permissionsToRemove.get(i));
135         }
136     }
137 
addReadPermission(UriPermission perm)138     public void addReadPermission(UriPermission perm) {
139         synchronized (this) {
140             if (mReadPerms == null) {
141                 mReadPerms = Sets.newArraySet();
142             }
143             mReadPerms.add(perm);
144         }
145     }
146 
addWritePermission(UriPermission perm)147     public void addWritePermission(UriPermission perm) {
148         synchronized (this) {
149             if (mWritePerms == null) {
150                 mWritePerms = Sets.newArraySet();
151             }
152             mWritePerms.add(perm);
153         }
154     }
155 
removeReadPermission(UriPermission perm)156     public void removeReadPermission(UriPermission perm) {
157         synchronized (this) {
158             if (mReadPerms != null) {
159                 mReadPerms.remove(perm);
160                 if (mReadPerms.isEmpty()) {
161                     mReadPerms = null;
162                 }
163             }
164         }
165     }
166 
removeWritePermission(UriPermission perm)167     public void removeWritePermission(UriPermission perm) {
168         synchronized (this) {
169             if (mWritePerms != null) {
170                 mWritePerms.remove(perm);
171                 if (mWritePerms.isEmpty()) {
172                     mWritePerms = null;
173                 }
174             }
175         }
176     }
177 
dump(PrintWriter pw, String prefix)178     public void dump(PrintWriter pw, String prefix) {
179         synchronized (this) {
180             if (mReadPerms != null) {
181                 pw.print(prefix);
182                 pw.print("readUriPermissions=");
183                 pw.println(mReadPerms);
184             }
185             if (mWritePerms != null) {
186                 pw.print(prefix);
187                 pw.print("writeUriPermissions=");
188                 pw.println(mWritePerms);
189             }
190         }
191     }
192 
dumpDebug(ProtoOutputStream proto, long fieldId)193     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
194         long token = proto.start(fieldId);
195         proto.write(UriPermissionOwnerProto.OWNER, mOwner.toString());
196         synchronized (this) {
197             if (mReadPerms != null) {
198                 for (UriPermission p : mReadPerms) {
199                     p.uri.dumpDebug(proto, UriPermissionOwnerProto.READ_PERMS);
200                 }
201             }
202             if (mWritePerms != null) {
203                 for (UriPermission p : mWritePerms) {
204                     p.uri.dumpDebug(proto, UriPermissionOwnerProto.WRITE_PERMS);
205                 }
206             }
207         }
208         proto.end(token);
209     }
210 
211     @Override
toString()212     public String toString() {
213         return mOwner.toString();
214     }
215 }
216