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        final int oldModeFlags = modeFlags;
98        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
99
100        if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
101            Slog.d(TAG,
102                    "Permission for " + targetPkg + " to " + uri + " is changing from 0x"
103                            + Integer.toHexString(oldModeFlags) + " to 0x"
104                            + Integer.toHexString(modeFlags),
105                    new Throwable());
106        }
107    }
108
109    /**
110     * Initialize persisted modes as read from file. This doesn't issue any
111     * global or owner grants.
112     */
113    void initPersistedModes(int modeFlags, long createdTime) {
114        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
115                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
116
117        persistableModeFlags = modeFlags;
118        persistedModeFlags = modeFlags;
119        persistedCreateTime = createdTime;
120
121        updateModeFlags();
122    }
123
124    void grantModes(int modeFlags, UriPermissionOwner owner) {
125        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
126        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
127                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
128
129        if (persistable) {
130            persistableModeFlags |= modeFlags;
131        }
132
133        if (owner == null) {
134            globalModeFlags |= modeFlags;
135        } else {
136            if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
137                addReadOwner(owner);
138            }
139            if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
140                addWriteOwner(owner);
141            }
142        }
143
144        updateModeFlags();
145    }
146
147    /**
148     * @return if mode changes should trigger persisting.
149     */
150    boolean takePersistableModes(int modeFlags) {
151        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
152                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
153
154        if ((modeFlags & persistableModeFlags) != modeFlags) {
155            Slog.w(TAG, "Requested flags 0x"
156                    + Integer.toHexString(modeFlags) + ", but only 0x"
157                    + Integer.toHexString(persistableModeFlags) + " are allowed");
158            return false;
159        }
160
161        final int before = persistedModeFlags;
162        persistedModeFlags |= (persistableModeFlags & modeFlags);
163
164        if (persistedModeFlags != 0) {
165            persistedCreateTime = System.currentTimeMillis();
166        }
167
168        updateModeFlags();
169        return persistedModeFlags != before;
170    }
171
172    boolean releasePersistableModes(int modeFlags) {
173        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
174                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
175
176        final int before = persistedModeFlags;
177
178        persistableModeFlags &= ~modeFlags;
179        persistedModeFlags &= ~modeFlags;
180
181        if (persistedModeFlags == 0) {
182            persistedCreateTime = INVALID_TIME;
183        }
184
185        updateModeFlags();
186        return persistedModeFlags != before;
187    }
188
189    /**
190     * @return if mode changes should trigger persisting.
191     */
192    boolean revokeModes(int modeFlags, boolean includingOwners) {
193        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
194        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
195                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
196
197        final int before = persistedModeFlags;
198
199        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
200            if (persistable) {
201                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
202                persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
203            }
204            globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
205            if (mReadOwners != null && includingOwners) {
206                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
207                for (UriPermissionOwner r : mReadOwners) {
208                    r.removeReadPermission(this);
209                }
210                mReadOwners = null;
211            }
212        }
213        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
214            if (persistable) {
215                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
216                persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
217            }
218            globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
219            if (mWriteOwners != null && includingOwners) {
220                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
221                for (UriPermissionOwner r : mWriteOwners) {
222                    r.removeWritePermission(this);
223                }
224                mWriteOwners = null;
225            }
226        }
227
228        if (persistedModeFlags == 0) {
229            persistedCreateTime = INVALID_TIME;
230        }
231
232        updateModeFlags();
233        return persistedModeFlags != before;
234    }
235
236    /**
237     * Return strength of this permission grant for the given flags.
238     */
239    public int getStrength(int modeFlags) {
240        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
241                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
242        if ((persistableModeFlags & modeFlags) == modeFlags) {
243            return STRENGTH_PERSISTABLE;
244        } else if ((globalModeFlags & modeFlags) == modeFlags) {
245            return STRENGTH_GLOBAL;
246        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
247            return STRENGTH_OWNED;
248        } else {
249            return STRENGTH_NONE;
250        }
251    }
252
253    private void addReadOwner(UriPermissionOwner owner) {
254        if (mReadOwners == null) {
255            mReadOwners = Sets.newArraySet();
256            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
257            updateModeFlags();
258        }
259        if (mReadOwners.add(owner)) {
260            owner.addReadPermission(this);
261        }
262    }
263
264    /**
265     * Remove given read owner, updating {@Link #modeFlags} as needed.
266     */
267    void removeReadOwner(UriPermissionOwner owner) {
268        if (!mReadOwners.remove(owner)) {
269            Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
270        }
271        if (mReadOwners.size() == 0) {
272            mReadOwners = null;
273            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
274            updateModeFlags();
275        }
276    }
277
278    private void addWriteOwner(UriPermissionOwner owner) {
279        if (mWriteOwners == null) {
280            mWriteOwners = Sets.newArraySet();
281            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
282            updateModeFlags();
283        }
284        if (mWriteOwners.add(owner)) {
285            owner.addWritePermission(this);
286        }
287    }
288
289    /**
290     * Remove given write owner, updating {@Link #modeFlags} as needed.
291     */
292    void removeWriteOwner(UriPermissionOwner owner) {
293        if (!mWriteOwners.remove(owner)) {
294            Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
295        }
296        if (mWriteOwners.size() == 0) {
297            mWriteOwners = null;
298            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
299            updateModeFlags();
300        }
301    }
302
303    @Override
304    public String toString() {
305        if (stringName != null) {
306            return stringName;
307        }
308        StringBuilder sb = new StringBuilder(128);
309        sb.append("UriPermission{");
310        sb.append(Integer.toHexString(System.identityHashCode(this)));
311        sb.append(' ');
312        sb.append(uri);
313        sb.append('}');
314        return stringName = sb.toString();
315    }
316
317    void dump(PrintWriter pw, String prefix) {
318        pw.print(prefix);
319        pw.print("targetUserId=" + targetUserId);
320        pw.print(" sourcePkg=" + sourcePkg);
321        pw.println(" targetPkg=" + targetPkg);
322
323        pw.print(prefix);
324        pw.print("mode=0x" + Integer.toHexString(modeFlags));
325        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
326        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
327        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
328        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
329        if (persistedCreateTime != INVALID_TIME) {
330            pw.print(" persistedCreate=" + persistedCreateTime);
331        }
332        pw.println();
333
334        if (mReadOwners != null) {
335            pw.print(prefix);
336            pw.println("readOwners:");
337            for (UriPermissionOwner owner : mReadOwners) {
338                pw.print(prefix);
339                pw.println("  * " + owner);
340            }
341        }
342        if (mWriteOwners != null) {
343            pw.print(prefix);
344            pw.println("writeOwners:");
345            for (UriPermissionOwner owner : mReadOwners) {
346                pw.print(prefix);
347                pw.println("  * " + owner);
348            }
349        }
350    }
351
352    public static class PersistedTimeComparator implements Comparator<UriPermission> {
353        @Override
354        public int compare(UriPermission lhs, UriPermission rhs) {
355            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
356        }
357    }
358
359    /**
360     * Snapshot of {@link UriPermission} with frozen
361     * {@link UriPermission#persistedModeFlags} state.
362     */
363    public static class Snapshot {
364        final int targetUserId;
365        final String sourcePkg;
366        final String targetPkg;
367        final GrantUri uri;
368        final int persistedModeFlags;
369        final long persistedCreateTime;
370
371        private Snapshot(UriPermission perm) {
372            this.targetUserId = perm.targetUserId;
373            this.sourcePkg = perm.sourcePkg;
374            this.targetPkg = perm.targetPkg;
375            this.uri = perm.uri;
376            this.persistedModeFlags = perm.persistedModeFlags;
377            this.persistedCreateTime = perm.persistedCreateTime;
378        }
379    }
380
381    public Snapshot snapshot() {
382        return new Snapshot(this);
383    }
384
385    public android.content.UriPermission buildPersistedPublicApiObject() {
386        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
387    }
388}
389