1/*
2 * Copyright (C) 2006 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.am;
18
19import android.app.GrantedUriPermission;
20import android.content.Intent;
21import android.os.Binder;
22import android.os.UserHandle;
23import android.util.ArraySet;
24import android.util.Log;
25import android.util.Slog;
26
27import com.android.server.am.ActivityManagerService.GrantUri;
28import com.google.android.collect.Sets;
29
30import java.io.PrintWriter;
31import java.util.Comparator;
32
33/**
34 * Description of a permission granted to an app to access a particular URI.
35 *
36 * CTS tests for this functionality can be run with "runtest cts-appsecurity".
37 *
38 * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
39 *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
40 */
41final class UriPermission {
42    private static final String TAG = "UriPermission";
43
44    public static final int STRENGTH_NONE = 0;
45    public static final int STRENGTH_OWNED = 1;
46    public static final int STRENGTH_GLOBAL = 2;
47    public static final int STRENGTH_PERSISTABLE = 3;
48
49    final int targetUserId;
50    final String sourcePkg;
51    final String targetPkg;
52
53    /** Cached UID of {@link #targetPkg}; should not be persisted */
54    final int targetUid;
55
56    final GrantUri uri;
57
58    /**
59     * Allowed modes. All permission enforcement should use this field. Must
60     * always be a combination of {@link #ownedModeFlags},
61     * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
62     * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
63     * the owning class.
64     */
65    int modeFlags = 0;
66
67    /** Allowed modes with active owner. */
68    int ownedModeFlags = 0;
69    /** Allowed modes without explicit owner. */
70    int globalModeFlags = 0;
71    /** Allowed modes that have been offered for possible persisting. */
72    int persistableModeFlags = 0;
73
74    /** Allowed modes that should be persisted across device boots. */
75    int persistedModeFlags = 0;
76
77    /**
78     * Timestamp when {@link #persistedModeFlags} was first defined in
79     * {@link System#currentTimeMillis()} time base.
80     */
81    long persistedCreateTime = INVALID_TIME;
82
83    private static final long INVALID_TIME = Long.MIN_VALUE;
84
85    private ArraySet<UriPermissionOwner> mReadOwners;
86    private ArraySet<UriPermissionOwner> mWriteOwners;
87
88    private String stringName;
89
90    UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
91        this.targetUserId = UserHandle.getUserId(targetUid);
92        this.sourcePkg = sourcePkg;
93        this.targetPkg = targetPkg;
94        this.targetUid = targetUid;
95        this.uri = uri;
96    }
97
98    private void updateModeFlags() {
99        final int oldModeFlags = modeFlags;
100        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
101
102        if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
103            Slog.d(TAG,
104                    "Permission for " + targetPkg + " to " + uri + " is changing from 0x"
105                            + Integer.toHexString(oldModeFlags) + " to 0x"
106                            + Integer.toHexString(modeFlags) + " via calling UID "
107                            + Binder.getCallingUid() + " PID " + Binder.getCallingPid(),
108                    new Throwable());
109        }
110    }
111
112    /**
113     * Initialize persisted modes as read from file. This doesn't issue any
114     * global or owner grants.
115     */
116    void initPersistedModes(int modeFlags, long createdTime) {
117        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
118                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
119
120        persistableModeFlags = modeFlags;
121        persistedModeFlags = modeFlags;
122        persistedCreateTime = createdTime;
123
124        updateModeFlags();
125    }
126
127    void grantModes(int modeFlags, UriPermissionOwner owner) {
128        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
129        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
130                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
131
132        if (persistable) {
133            persistableModeFlags |= modeFlags;
134        }
135
136        if (owner == null) {
137            globalModeFlags |= modeFlags;
138        } else {
139            if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
140                addReadOwner(owner);
141            }
142            if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
143                addWriteOwner(owner);
144            }
145        }
146
147        updateModeFlags();
148    }
149
150    /**
151     * @return if mode changes should trigger persisting.
152     */
153    boolean takePersistableModes(int modeFlags) {
154        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
155                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
156
157        if ((modeFlags & persistableModeFlags) != modeFlags) {
158            Slog.w(TAG, "Requested flags 0x"
159                    + Integer.toHexString(modeFlags) + ", but only 0x"
160                    + Integer.toHexString(persistableModeFlags) + " are allowed");
161            return false;
162        }
163
164        final int before = persistedModeFlags;
165        persistedModeFlags |= (persistableModeFlags & modeFlags);
166
167        if (persistedModeFlags != 0) {
168            persistedCreateTime = System.currentTimeMillis();
169        }
170
171        updateModeFlags();
172        return persistedModeFlags != before;
173    }
174
175    boolean releasePersistableModes(int modeFlags) {
176        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
177                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
178
179        final int before = persistedModeFlags;
180
181        persistableModeFlags &= ~modeFlags;
182        persistedModeFlags &= ~modeFlags;
183
184        if (persistedModeFlags == 0) {
185            persistedCreateTime = INVALID_TIME;
186        }
187
188        updateModeFlags();
189        return persistedModeFlags != before;
190    }
191
192    /**
193     * @return if mode changes should trigger persisting.
194     */
195    boolean revokeModes(int modeFlags, boolean includingOwners) {
196        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
197        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
198                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
199
200        final int before = persistedModeFlags;
201
202        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
203            if (persistable) {
204                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
205                persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
206            }
207            globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
208            if (mReadOwners != null && includingOwners) {
209                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
210                for (UriPermissionOwner r : mReadOwners) {
211                    r.removeReadPermission(this);
212                }
213                mReadOwners = null;
214            }
215        }
216        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
217            if (persistable) {
218                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
219                persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
220            }
221            globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
222            if (mWriteOwners != null && includingOwners) {
223                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
224                for (UriPermissionOwner r : mWriteOwners) {
225                    r.removeWritePermission(this);
226                }
227                mWriteOwners = null;
228            }
229        }
230
231        if (persistedModeFlags == 0) {
232            persistedCreateTime = INVALID_TIME;
233        }
234
235        updateModeFlags();
236        return persistedModeFlags != before;
237    }
238
239    /**
240     * Return strength of this permission grant for the given flags.
241     */
242    public int getStrength(int modeFlags) {
243        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
244                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
245        if ((persistableModeFlags & modeFlags) == modeFlags) {
246            return STRENGTH_PERSISTABLE;
247        } else if ((globalModeFlags & modeFlags) == modeFlags) {
248            return STRENGTH_GLOBAL;
249        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
250            return STRENGTH_OWNED;
251        } else {
252            return STRENGTH_NONE;
253        }
254    }
255
256    private void addReadOwner(UriPermissionOwner owner) {
257        if (mReadOwners == null) {
258            mReadOwners = Sets.newArraySet();
259            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
260            updateModeFlags();
261        }
262        if (mReadOwners.add(owner)) {
263            owner.addReadPermission(this);
264        }
265    }
266
267    /**
268     * Remove given read owner, updating {@Link #modeFlags} as needed.
269     */
270    void removeReadOwner(UriPermissionOwner owner) {
271        if (!mReadOwners.remove(owner)) {
272            Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
273        }
274        if (mReadOwners.size() == 0) {
275            mReadOwners = null;
276            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
277            updateModeFlags();
278        }
279    }
280
281    private void addWriteOwner(UriPermissionOwner owner) {
282        if (mWriteOwners == null) {
283            mWriteOwners = Sets.newArraySet();
284            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
285            updateModeFlags();
286        }
287        if (mWriteOwners.add(owner)) {
288            owner.addWritePermission(this);
289        }
290    }
291
292    /**
293     * Remove given write owner, updating {@Link #modeFlags} as needed.
294     */
295    void removeWriteOwner(UriPermissionOwner owner) {
296        if (!mWriteOwners.remove(owner)) {
297            Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
298        }
299        if (mWriteOwners.size() == 0) {
300            mWriteOwners = null;
301            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
302            updateModeFlags();
303        }
304    }
305
306    @Override
307    public String toString() {
308        if (stringName != null) {
309            return stringName;
310        }
311        StringBuilder sb = new StringBuilder(128);
312        sb.append("UriPermission{");
313        sb.append(Integer.toHexString(System.identityHashCode(this)));
314        sb.append(' ');
315        sb.append(uri);
316        sb.append('}');
317        return stringName = sb.toString();
318    }
319
320    void dump(PrintWriter pw, String prefix) {
321        pw.print(prefix);
322        pw.print("targetUserId=" + targetUserId);
323        pw.print(" sourcePkg=" + sourcePkg);
324        pw.println(" targetPkg=" + targetPkg);
325
326        pw.print(prefix);
327        pw.print("mode=0x" + Integer.toHexString(modeFlags));
328        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
329        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
330        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
331        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
332        if (persistedCreateTime != INVALID_TIME) {
333            pw.print(" persistedCreate=" + persistedCreateTime);
334        }
335        pw.println();
336
337        if (mReadOwners != null) {
338            pw.print(prefix);
339            pw.println("readOwners:");
340            for (UriPermissionOwner owner : mReadOwners) {
341                pw.print(prefix);
342                pw.println("  * " + owner);
343            }
344        }
345        if (mWriteOwners != null) {
346            pw.print(prefix);
347            pw.println("writeOwners:");
348            for (UriPermissionOwner owner : mReadOwners) {
349                pw.print(prefix);
350                pw.println("  * " + owner);
351            }
352        }
353    }
354
355    public static class PersistedTimeComparator implements Comparator<UriPermission> {
356        @Override
357        public int compare(UriPermission lhs, UriPermission rhs) {
358            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
359        }
360    }
361
362    /**
363     * Snapshot of {@link UriPermission} with frozen
364     * {@link UriPermission#persistedModeFlags} state.
365     */
366    public static class Snapshot {
367        final int targetUserId;
368        final String sourcePkg;
369        final String targetPkg;
370        final GrantUri uri;
371        final int persistedModeFlags;
372        final long persistedCreateTime;
373
374        private Snapshot(UriPermission perm) {
375            this.targetUserId = perm.targetUserId;
376            this.sourcePkg = perm.sourcePkg;
377            this.targetPkg = perm.targetPkg;
378            this.uri = perm.uri;
379            this.persistedModeFlags = perm.persistedModeFlags;
380            this.persistedCreateTime = perm.persistedCreateTime;
381        }
382    }
383
384    public Snapshot snapshot() {
385        return new Snapshot(this);
386    }
387
388    public android.content.UriPermission buildPersistedPublicApiObject() {
389        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
390    }
391
392    public GrantedUriPermission buildGrantedUriPermission() {
393        return new GrantedUriPermission(uri.uri, targetPkg);
394    }
395}
396