1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.server.slice;
16
17import android.annotation.NonNull;
18import android.util.ArrayMap;
19import android.util.ArraySet;
20import android.util.Slog;
21
22import com.android.server.slice.DirtyTracker.Persistable;
23import com.android.server.slice.SlicePermissionManager.PkgUser;
24
25import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27import org.xmlpull.v1.XmlSerializer;
28
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.Objects;
33
34public class SliceProviderPermissions implements DirtyTracker, Persistable {
35
36    private static final String TAG = "SliceProviderPermissions";
37
38    static final String TAG_PROVIDER = "provider";
39    private static final String TAG_AUTHORITY = "authority";
40    private static final String TAG_PKG = "pkg";
41    private static final String NAMESPACE = null;
42
43    private static final String ATTR_PKG = "pkg";
44    private static final String ATTR_AUTHORITY = "authority";
45
46    private final PkgUser mPkg;
47    private final ArrayMap<String, SliceAuthority> mAuths = new ArrayMap<>();
48    private final DirtyTracker mTracker;
49
50    public SliceProviderPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
51        mPkg = pkg;
52        mTracker = tracker;
53    }
54
55    public PkgUser getPkg() {
56        return mPkg;
57    }
58
59    public synchronized Collection<SliceAuthority> getAuthorities() {
60        return new ArrayList<>(mAuths.values());
61    }
62
63    public synchronized SliceAuthority getOrCreateAuthority(String authority) {
64        SliceAuthority ret = mAuths.get(authority);
65        if (ret == null) {
66            ret = new SliceAuthority(authority, this);
67            mAuths.put(authority, ret);
68            onPersistableDirty(ret);
69        }
70        return ret;
71    }
72
73    @Override
74    public void onPersistableDirty(Persistable obj) {
75        mTracker.onPersistableDirty(this);
76    }
77
78    @Override
79    public String getFileName() {
80        return getFileName(mPkg);
81    }
82
83    public synchronized void writeTo(XmlSerializer out) throws IOException {
84        out.startTag(NAMESPACE, TAG_PROVIDER);
85        out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString());
86
87        final int N = mAuths.size();
88        for (int i = 0; i < N; i++) {
89            out.startTag(NAMESPACE, TAG_AUTHORITY);
90            out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority);
91
92            mAuths.valueAt(i).writeTo(out);
93
94            out.endTag(NAMESPACE, TAG_AUTHORITY);
95        }
96
97        out.endTag(NAMESPACE, TAG_PROVIDER);
98    }
99
100    public static SliceProviderPermissions createFrom(XmlPullParser parser, DirtyTracker tracker)
101            throws XmlPullParserException, IOException {
102        // Get to the beginning of the provider.
103        while (parser.getEventType() != XmlPullParser.START_TAG
104                || !TAG_PROVIDER.equals(parser.getName())) {
105            parser.next();
106        }
107        int depth = parser.getDepth();
108        PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
109        SliceProviderPermissions provider = new SliceProviderPermissions(pkgUser, tracker);
110        parser.next();
111
112        while (parser.getDepth() > depth) {
113            if (parser.getEventType() == XmlPullParser.START_TAG
114                    && TAG_AUTHORITY.equals(parser.getName())) {
115                try {
116                    SliceAuthority authority = new SliceAuthority(
117                            parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), provider);
118                    authority.readFrom(parser);
119                    provider.mAuths.put(authority.getAuthority(), authority);
120                } catch (IllegalArgumentException e) {
121                    Slog.e(TAG, "Couldn't read PkgUser", e);
122                }
123            }
124
125            parser.next();
126        }
127        return provider;
128    }
129
130    public static String getFileName(PkgUser pkg) {
131        return String.format("provider_%s", pkg.toString());
132    }
133
134    public static class SliceAuthority implements Persistable {
135        private final String mAuthority;
136        private final DirtyTracker mTracker;
137        private final ArraySet<PkgUser> mPkgs = new ArraySet<>();
138
139        public SliceAuthority(String authority, DirtyTracker tracker) {
140            mAuthority = authority;
141            mTracker = tracker;
142        }
143
144        public String getAuthority() {
145            return mAuthority;
146        }
147
148        public synchronized void addPkg(PkgUser pkg) {
149            if (mPkgs.add(pkg)) {
150                mTracker.onPersistableDirty(this);
151            }
152        }
153
154        public synchronized void removePkg(PkgUser pkg) {
155            if (mPkgs.remove(pkg)) {
156                mTracker.onPersistableDirty(this);
157            }
158        }
159
160        public synchronized Collection<PkgUser> getPkgs() {
161            return new ArraySet<>(mPkgs);
162        }
163
164        @Override
165        public String getFileName() {
166            return null;
167        }
168
169        public synchronized void writeTo(XmlSerializer out) throws IOException {
170            final int N = mPkgs.size();
171            for (int i = 0; i < N; i++) {
172                out.startTag(NAMESPACE, TAG_PKG);
173                out.text(mPkgs.valueAt(i).toString());
174                out.endTag(NAMESPACE, TAG_PKG);
175            }
176        }
177
178        public synchronized void readFrom(XmlPullParser parser)
179                throws IOException, XmlPullParserException {
180            parser.next();
181            int depth = parser.getDepth();
182            while (parser.getDepth() >= depth) {
183                if (parser.getEventType() == XmlPullParser.START_TAG
184                        && TAG_PKG.equals(parser.getName())) {
185                    mPkgs.add(new PkgUser(parser.nextText()));
186                }
187                parser.next();
188            }
189        }
190
191        @Override
192        public boolean equals(Object obj) {
193            if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
194            SliceAuthority other = (SliceAuthority) obj;
195            return Objects.equals(mAuthority, other.mAuthority)
196                    && Objects.equals(mPkgs, other.mPkgs);
197        }
198
199        @Override
200        public String toString() {
201            return String.format("(%s: %s)", mAuthority, mPkgs.toString());
202        }
203    }
204}
205