• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
17 package com.android.server.appop;
18 
19 import static android.app.AppOpsManager.opToDefaultMode;
20 
21 import android.annotation.NonNull;
22 import android.app.AppOpsManager;
23 import android.os.UserHandle;
24 import android.util.ArrayMap;
25 import android.util.AtomicFile;
26 import android.util.Slog;
27 import android.util.SparseArray;
28 import android.util.SparseIntArray;
29 import android.util.Xml;
30 
31 import com.android.internal.util.XmlUtils;
32 import com.android.modules.utils.TypedXmlPullParser;
33 
34 import org.xmlpull.v1.XmlPullParser;
35 import org.xmlpull.v1.XmlPullParserException;
36 
37 import java.io.FileInputStream;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 
41 class LegacyAppOpStateParser {
42     static final String TAG = LegacyAppOpStateParser.class.getSimpleName();
43 
44     private static final int NO_FILE_VERSION = -2;
45     private static final int NO_VERSION = -1;
46 
47     /**
48      * Reads legacy app-ops data into given maps.
49      */
readState(AtomicFile file, SparseArray<SparseIntArray> uidModes, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)50     public int readState(AtomicFile file, SparseArray<SparseIntArray> uidModes,
51             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes) {
52         try (FileInputStream stream = file.openRead()) {
53             SparseArray<SparseIntArray> parsedUidModes = new SparseArray<>();
54             SparseArray<ArrayMap<String, SparseIntArray>> parsedUserPackageModes =
55                     new SparseArray<>();
56 
57             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
58             int type;
59             while ((type = parser.next()) != XmlPullParser.START_TAG
60                     && type != XmlPullParser.END_DOCUMENT) {
61                 // Parse next until we reach the start or end
62             }
63 
64             if (type != XmlPullParser.START_TAG) {
65                 throw new IllegalStateException("no start tag found");
66             }
67 
68             int versionAtBoot = parser.getAttributeInt(null, "v", NO_VERSION);
69 
70             int outerDepth = parser.getDepth();
71             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
72                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
73                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
74                     continue;
75                 }
76 
77                 String tagName = parser.getName();
78                 if (tagName.equals("pkg")) {
79                     // version 2 has the structure pkg -> uid -> op ->
80                     // in version 3, since pkg and uid states are kept completely
81                     // independent we switch to user -> pkg -> op
82                     readPackage(parser, parsedUserPackageModes);
83                 } else if (tagName.equals("uid")) {
84                     readUidOps(parser, parsedUidModes);
85                 } else if (tagName.equals("user")) {
86                     readUser(parser, parsedUserPackageModes);
87                 } else {
88                     Slog.w(TAG, "Unknown element under <app-ops>: "
89                             + parser.getName());
90                     XmlUtils.skipCurrentTag(parser);
91                 }
92             }
93 
94             // Parsing is complete, copy all parsed values to output
95             final int parsedUidModesSize = parsedUidModes.size();
96             for (int i = 0; i < parsedUidModesSize; i++) {
97                 uidModes.put(parsedUidModes.keyAt(i), parsedUidModes.valueAt(i));
98             }
99             final int parsedUserPackageModesSize = parsedUserPackageModes.size();
100             for (int i = 0; i < parsedUserPackageModesSize; i++) {
101                 userPackageModes.put(parsedUserPackageModes.keyAt(i),
102                                      parsedUserPackageModes.valueAt(i));
103             }
104 
105             return versionAtBoot;
106         } catch (FileNotFoundException e) {
107             Slog.i(TAG, "No existing app ops " + file.getBaseFile() + "; starting empty");
108         } catch (Exception e) {
109             // All exceptions must be caught, otherwise device will not be able to boot
110             Slog.wtf(TAG, "Failed parsing " + e);
111         }
112         return NO_FILE_VERSION;
113     }
114 
readPackage(TypedXmlPullParser parser, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)115     private void readPackage(TypedXmlPullParser parser,
116             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
117             throws NumberFormatException, XmlPullParserException, IOException {
118         String pkgName = parser.getAttributeValue(null, "n");
119         int outerDepth = parser.getDepth();
120         int type;
121         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
122                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
123             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
124                 continue;
125             }
126 
127             String tagName = parser.getName();
128             if (tagName.equals("uid")) {
129                 readPackageUid(parser, pkgName, userPackageModes);
130             } else {
131                 Slog.w(TAG, "Unknown element under <pkg>: "
132                         + parser.getName());
133                 XmlUtils.skipCurrentTag(parser);
134             }
135         }
136     }
137 
readPackageUid(TypedXmlPullParser parser, String pkgName, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)138     private void readPackageUid(TypedXmlPullParser parser, String pkgName,
139             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
140             throws NumberFormatException, XmlPullParserException, IOException {
141         int userId = UserHandle.getUserId(parser.getAttributeInt(null, "n"));
142         int outerDepth = parser.getDepth();
143         int type;
144         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
145                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
146             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
147                 continue;
148             }
149 
150             String tagName = parser.getName();
151             if (tagName.equals("op")) {
152                 readOp(parser, userId, pkgName, userPackageModes);
153             } else {
154                 Slog.w(TAG, "Unknown element under <pkg>: "
155                         + parser.getName());
156                 XmlUtils.skipCurrentTag(parser);
157             }
158         }
159     }
160 
readUidOps(TypedXmlPullParser parser, SparseArray<SparseIntArray> uidModes)161     private void readUidOps(TypedXmlPullParser parser, SparseArray<SparseIntArray> uidModes)
162             throws NumberFormatException,
163             XmlPullParserException, IOException {
164         final int uid = parser.getAttributeInt(null, "n");
165         SparseIntArray modes = uidModes.get(uid);
166         if (modes == null) {
167             modes = new SparseIntArray();
168             uidModes.put(uid, modes);
169         }
170 
171         int outerDepth = parser.getDepth();
172         int type;
173         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
174                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
175             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
176                 continue;
177             }
178 
179             String tagName = parser.getName();
180             if (tagName.equals("op")) {
181                 final int code = parser.getAttributeInt(null, "n");
182                 final int mode = parser.getAttributeInt(null, "m");
183 
184                 if (mode != opToDefaultMode(code)) {
185                     modes.put(code, mode);
186                 }
187             } else {
188                 Slog.w(TAG, "Unknown element under <uid>: "
189                         + parser.getName());
190                 XmlUtils.skipCurrentTag(parser);
191             }
192         }
193     }
194 
readUser(TypedXmlPullParser parser, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)195     private void readUser(TypedXmlPullParser parser,
196             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
197             throws NumberFormatException, XmlPullParserException, IOException {
198         int userId = parser.getAttributeInt(null, "n");
199         int outerDepth = parser.getDepth();
200         int type;
201         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
202                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
203             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
204                 continue;
205             }
206 
207             String tagName = parser.getName();
208             if (tagName.equals("pkg")) {
209                 readPackageOp(parser, userId, userPackageModes);
210             } else {
211                 Slog.w(TAG, "Unknown element under <user>: "
212                         + parser.getName());
213                 XmlUtils.skipCurrentTag(parser);
214             }
215         }
216     }
217 
218     // read package tag refactored in Android U
readPackageOp(TypedXmlPullParser parser, int userId, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)219     private void readPackageOp(TypedXmlPullParser parser, int userId,
220             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
221             throws NumberFormatException, XmlPullParserException, IOException {
222         String pkgName = parser.getAttributeValue(null, "n");
223         int outerDepth = parser.getDepth();
224         int type;
225         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
226                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
227             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
228                 continue;
229             }
230 
231             String tagName = parser.getName();
232             if (tagName.equals("op")) {
233                 readOp(parser, userId, pkgName, userPackageModes);
234             } else {
235                 Slog.w(TAG, "Unknown element under <pkg>: "
236                         + parser.getName());
237                 XmlUtils.skipCurrentTag(parser);
238             }
239         }
240     }
241 
readOp(TypedXmlPullParser parser, int userId, @NonNull String pkgName, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)242     private void readOp(TypedXmlPullParser parser, int userId, @NonNull String pkgName,
243             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
244             throws NumberFormatException, XmlPullParserException {
245         final int opCode = parser.getAttributeInt(null, "n");
246         final int defaultMode = AppOpsManager.opToDefaultMode(opCode);
247         final int mode = parser.getAttributeInt(null, "m", defaultMode);
248 
249         if (mode != defaultMode) {
250             ArrayMap<String, SparseIntArray> packageModes = userPackageModes.get(userId);
251             if (packageModes == null) {
252                 packageModes = new ArrayMap<>();
253                 userPackageModes.put(userId, packageModes);
254             }
255 
256             SparseIntArray modes = packageModes.get(pkgName);
257             if (modes == null) {
258                 modes = new SparseIntArray();
259                 packageModes.put(pkgName, modes);
260             }
261 
262             modes.put(opCode, mode);
263         }
264     }
265 }
266