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 
15 package com.android.server.slice;
16 
17 import android.annotation.NonNull;
18 import android.util.ArrayMap;
19 import android.util.ArraySet;
20 import android.util.Slog;
21 
22 import com.android.server.slice.DirtyTracker.Persistable;
23 import com.android.server.slice.SlicePermissionManager.PkgUser;
24 
25 import org.xmlpull.v1.XmlPullParser;
26 import org.xmlpull.v1.XmlPullParserException;
27 import org.xmlpull.v1.XmlSerializer;
28 
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Objects;
33 
34 public 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 
SliceProviderPermissions(@onNull PkgUser pkg, @NonNull DirtyTracker tracker)50     public SliceProviderPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
51         mPkg = pkg;
52         mTracker = tracker;
53     }
54 
getPkg()55     public PkgUser getPkg() {
56         return mPkg;
57     }
58 
getAuthorities()59     public synchronized Collection<SliceAuthority> getAuthorities() {
60         return new ArrayList<>(mAuths.values());
61     }
62 
getOrCreateAuthority(String authority)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
onPersistableDirty(Persistable obj)74     public void onPersistableDirty(Persistable obj) {
75         mTracker.onPersistableDirty(this);
76     }
77 
78     @Override
getFileName()79     public String getFileName() {
80         return getFileName(mPkg);
81     }
82 
writeTo(XmlSerializer out)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 
createFrom(XmlPullParser parser, DirtyTracker tracker)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 
getFileName(PkgUser pkg)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 
SliceAuthority(String authority, DirtyTracker tracker)139         public SliceAuthority(String authority, DirtyTracker tracker) {
140             mAuthority = authority;
141             mTracker = tracker;
142         }
143 
getAuthority()144         public String getAuthority() {
145             return mAuthority;
146         }
147 
addPkg(PkgUser pkg)148         public synchronized void addPkg(PkgUser pkg) {
149             if (mPkgs.add(pkg)) {
150                 mTracker.onPersistableDirty(this);
151             }
152         }
153 
removePkg(PkgUser pkg)154         public synchronized void removePkg(PkgUser pkg) {
155             if (mPkgs.remove(pkg)) {
156                 mTracker.onPersistableDirty(this);
157             }
158         }
159 
getPkgs()160         public synchronized Collection<PkgUser> getPkgs() {
161             return new ArraySet<>(mPkgs);
162         }
163 
164         @Override
getFileName()165         public String getFileName() {
166             return null;
167         }
168 
writeTo(XmlSerializer out)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 
readFrom(XmlPullParser parser)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
equals(Object obj)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
toString()200         public String toString() {
201             return String.format("(%s: %s)", mAuthority, mPkgs.toString());
202         }
203     }
204 }
205