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