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