• 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             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
54             int type;
55             while ((type = parser.next()) != XmlPullParser.START_TAG
56                     && type != XmlPullParser.END_DOCUMENT) {
57                 // Parse next until we reach the start or end
58             }
59 
60             if (type != XmlPullParser.START_TAG) {
61                 throw new IllegalStateException("no start tag found");
62             }
63 
64             int versionAtBoot = parser.getAttributeInt(null, "v", NO_VERSION);
65 
66             int outerDepth = parser.getDepth();
67             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
68                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
69                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
70                     continue;
71                 }
72 
73                 String tagName = parser.getName();
74                 if (tagName.equals("pkg")) {
75                     // version 2 has the structure pkg -> uid -> op ->
76                     // in version 3, since pkg and uid states are kept completely
77                     // independent we switch to user -> pkg -> op
78                     readPackage(parser, userPackageModes);
79                 } else if (tagName.equals("uid")) {
80                     readUidOps(parser, uidModes);
81                 } else if (tagName.equals("user")) {
82                     readUser(parser, userPackageModes);
83                 } else {
84                     Slog.w(TAG, "Unknown element under <app-ops>: "
85                             + parser.getName());
86                     XmlUtils.skipCurrentTag(parser);
87                 }
88             }
89             return versionAtBoot;
90         } catch (FileNotFoundException e) {
91             Slog.i(TAG, "No existing app ops " + file.getBaseFile() + "; starting empty");
92             return NO_FILE_VERSION;
93         } catch (XmlPullParserException e) {
94             throw new RuntimeException(e);
95         } catch (IOException e) {
96             throw new RuntimeException(e);
97         }
98     }
99 
readPackage(TypedXmlPullParser parser, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)100     private void readPackage(TypedXmlPullParser parser,
101             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
102             throws NumberFormatException, XmlPullParserException, IOException {
103         String pkgName = parser.getAttributeValue(null, "n");
104         int outerDepth = parser.getDepth();
105         int type;
106         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
107                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
108             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
109                 continue;
110             }
111 
112             String tagName = parser.getName();
113             if (tagName.equals("uid")) {
114                 readPackageUid(parser, pkgName, userPackageModes);
115             } else {
116                 Slog.w(TAG, "Unknown element under <pkg>: "
117                         + parser.getName());
118                 XmlUtils.skipCurrentTag(parser);
119             }
120         }
121     }
122 
readPackageUid(TypedXmlPullParser parser, String pkgName, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)123     private void readPackageUid(TypedXmlPullParser parser, String pkgName,
124             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
125             throws NumberFormatException, XmlPullParserException, IOException {
126         int userId = UserHandle.getUserId(parser.getAttributeInt(null, "n"));
127         int outerDepth = parser.getDepth();
128         int type;
129         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
130                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
131             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
132                 continue;
133             }
134 
135             String tagName = parser.getName();
136             if (tagName.equals("op")) {
137                 readOp(parser, userId, pkgName, userPackageModes);
138             } else {
139                 Slog.w(TAG, "Unknown element under <pkg>: "
140                         + parser.getName());
141                 XmlUtils.skipCurrentTag(parser);
142             }
143         }
144     }
145 
readUidOps(TypedXmlPullParser parser, SparseArray<SparseIntArray> uidModes)146     private void readUidOps(TypedXmlPullParser parser, SparseArray<SparseIntArray> uidModes)
147             throws NumberFormatException,
148             XmlPullParserException, IOException {
149         final int uid = parser.getAttributeInt(null, "n");
150         SparseIntArray modes = uidModes.get(uid);
151         if (modes == null) {
152             modes = new SparseIntArray();
153             uidModes.put(uid, modes);
154         }
155 
156         int outerDepth = parser.getDepth();
157         int type;
158         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
159                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
160             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
161                 continue;
162             }
163 
164             String tagName = parser.getName();
165             if (tagName.equals("op")) {
166                 final int code = parser.getAttributeInt(null, "n");
167                 final int mode = parser.getAttributeInt(null, "m");
168 
169                 if (mode != opToDefaultMode(code)) {
170                     modes.put(code, mode);
171                 }
172             } else {
173                 Slog.w(TAG, "Unknown element under <uid>: "
174                         + parser.getName());
175                 XmlUtils.skipCurrentTag(parser);
176             }
177         }
178     }
179 
readUser(TypedXmlPullParser parser, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)180     private void readUser(TypedXmlPullParser parser,
181             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
182             throws NumberFormatException, XmlPullParserException, IOException {
183         int userId = parser.getAttributeInt(null, "n");
184         int outerDepth = parser.getDepth();
185         int type;
186         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
187                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
188             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
189                 continue;
190             }
191 
192             String tagName = parser.getName();
193             if (tagName.equals("pkg")) {
194                 readPackageOp(parser, userId, userPackageModes);
195             } else {
196                 Slog.w(TAG, "Unknown element under <user>: "
197                         + parser.getName());
198                 XmlUtils.skipCurrentTag(parser);
199             }
200         }
201     }
202 
203     // read package tag refactored in Android U
readPackageOp(TypedXmlPullParser parser, int userId, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)204     private void readPackageOp(TypedXmlPullParser parser, int userId,
205             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
206             throws NumberFormatException, XmlPullParserException, IOException {
207         String pkgName = parser.getAttributeValue(null, "n");
208         int outerDepth = parser.getDepth();
209         int type;
210         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
211                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
212             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
213                 continue;
214             }
215 
216             String tagName = parser.getName();
217             if (tagName.equals("op")) {
218                 readOp(parser, userId, pkgName, userPackageModes);
219             } else {
220                 Slog.w(TAG, "Unknown element under <pkg>: "
221                         + parser.getName());
222                 XmlUtils.skipCurrentTag(parser);
223             }
224         }
225     }
226 
readOp(TypedXmlPullParser parser, int userId, @NonNull String pkgName, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)227     private void readOp(TypedXmlPullParser parser, int userId, @NonNull String pkgName,
228             SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes)
229             throws NumberFormatException, XmlPullParserException {
230         final int opCode = parser.getAttributeInt(null, "n");
231         final int defaultMode = AppOpsManager.opToDefaultMode(opCode);
232         final int mode = parser.getAttributeInt(null, "m", defaultMode);
233 
234         if (mode != defaultMode) {
235             ArrayMap<String, SparseIntArray> packageModes = userPackageModes.get(userId);
236             if (packageModes == null) {
237                 packageModes = new ArrayMap<>();
238                 userPackageModes.put(userId, packageModes);
239             }
240 
241             SparseIntArray modes = packageModes.get(pkgName);
242             if (modes == null) {
243                 modes = new SparseIntArray();
244                 packageModes.put(pkgName, modes);
245             }
246 
247             modes.put(opCode, mode);
248         }
249     }
250 }
251