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