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 android.annotation.NonNull;
20 import android.app.AppOpsManager;
21 import android.os.UserHandle;
22 import android.util.ArrayMap;
23 import android.util.AtomicFile;
24 import android.util.SparseArray;
25 import android.util.SparseIntArray;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.server.SystemServiceManager;
29 
30 import java.io.File;
31 import java.util.Collections;
32 import java.util.Map;
33 
34 /**
35  * Provider of legacy app-ops data for new permission subsystem.
36  *
37  * @hide
38  */
39 public class AppOpMigrationHelperImpl implements AppOpMigrationHelper {
40     private SparseArray<Map<Integer, Map<String, Integer>>> mAppIdAppOpModes = null;
41     private SparseArray<Map<String, Map<String, Integer>>> mPackageAppOpModes = null;
42     private int mVersionAtBoot;
43 
44     private final Object mLock = new Object();
45 
46     @Override
47     @GuardedBy("mLock")
48     @NonNull
getLegacyAppIdAppOpModes(int userId)49     public Map<Integer, Map<String, Integer>> getLegacyAppIdAppOpModes(int userId) {
50         synchronized (mLock) {
51             if (mAppIdAppOpModes == null) {
52                 readLegacyAppOpState();
53             }
54         }
55         return mAppIdAppOpModes.get(userId, Collections.emptyMap());
56     }
57 
58     @Override
59     @GuardedBy("mLock")
60     @NonNull
getLegacyPackageAppOpModes(int userId)61     public Map<String, Map<String, Integer>> getLegacyPackageAppOpModes(int userId) {
62         synchronized (mLock) {
63             if (mPackageAppOpModes == null) {
64                 readLegacyAppOpState();
65             }
66         }
67         return mPackageAppOpModes.get(userId, Collections.emptyMap());
68     }
69 
70     @GuardedBy("mLock")
readLegacyAppOpState()71     private void readLegacyAppOpState() {
72         final File systemDir = SystemServiceManager.ensureSystemDir();
73         AtomicFile appOpFile = new AtomicFile(new File(systemDir, "appops.xml"));
74 
75         final SparseArray<SparseIntArray> uidAppOpModes = new SparseArray<>();
76         final SparseArray<ArrayMap<String, SparseIntArray>> packageAppOpModes =
77                 new SparseArray<>();
78 
79         LegacyAppOpStateParser parser = new LegacyAppOpStateParser();
80         final int version = parser.readState(appOpFile, uidAppOpModes, packageAppOpModes);
81         // -1 No app ops data available
82         // 0 appops.xml exist w/o any version
83         switch (version) {
84             case -2:
85                 mVersionAtBoot = -1;
86                 break;
87             case -1:
88                 mVersionAtBoot = 0;
89                 break;
90             default:
91                 mVersionAtBoot = version;
92         }
93         mAppIdAppOpModes = getAppIdAppOpModes(uidAppOpModes);
94         mPackageAppOpModes = getPackageAppOpModes(packageAppOpModes);
95     }
96 
getAppIdAppOpModes( SparseArray<SparseIntArray> uidAppOpModes)97     private SparseArray<Map<Integer, Map<String, Integer>>> getAppIdAppOpModes(
98             SparseArray<SparseIntArray> uidAppOpModes) {
99         SparseArray<Map<Integer, Map<String, Integer>>> userAppIdAppOpModes = new SparseArray<>();
100 
101         int size = uidAppOpModes.size();
102         for (int uidIndex = 0; uidIndex < size; uidIndex++) {
103             int uid = uidAppOpModes.keyAt(uidIndex);
104             int userId = UserHandle.getUserId(uid);
105             Map<Integer, Map<String, Integer>> appIdAppOpModes = userAppIdAppOpModes.get(userId);
106             if (appIdAppOpModes == null) {
107                 appIdAppOpModes = new ArrayMap<>();
108                 userAppIdAppOpModes.put(userId, appIdAppOpModes);
109             }
110 
111             SparseIntArray appOpModes = uidAppOpModes.valueAt(uidIndex);
112             appIdAppOpModes.put(UserHandle.getAppId(uid), getAppOpModesForOpName(appOpModes));
113         }
114         return userAppIdAppOpModes;
115     }
116 
getPackageAppOpModes( SparseArray<ArrayMap<String, SparseIntArray>> legacyPackageAppOpModes)117     private SparseArray<Map<String, Map<String, Integer>>> getPackageAppOpModes(
118             SparseArray<ArrayMap<String, SparseIntArray>> legacyPackageAppOpModes) {
119         SparseArray<Map<String, Map<String, Integer>>> userPackageAppOpModes = new SparseArray<>();
120 
121         int usersSize = legacyPackageAppOpModes.size();
122         for (int userIndex = 0; userIndex < usersSize; userIndex++) {
123             int userId = legacyPackageAppOpModes.keyAt(userIndex);
124             Map<String, Map<String, Integer>> packageAppOpModes = userPackageAppOpModes.get(userId);
125             if (packageAppOpModes == null) {
126                 packageAppOpModes = new ArrayMap<>();
127                 userPackageAppOpModes.put(userId, packageAppOpModes);
128             }
129 
130             ArrayMap<String, SparseIntArray> legacyPackagesModes =
131                     legacyPackageAppOpModes.valueAt(userIndex);
132 
133             int packagesSize = legacyPackagesModes.size();
134             for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
135                 String packageName = legacyPackagesModes.keyAt(packageIndex);
136                 SparseIntArray modes = legacyPackagesModes.valueAt(packageIndex);
137                 packageAppOpModes.put(packageName, getAppOpModesForOpName(modes));
138             }
139         }
140         return userPackageAppOpModes;
141     }
142 
143     /**
144      * Converts the map from op code -> mode to op name -> mode.
145      */
getAppOpModesForOpName(SparseIntArray appOpCodeModes)146     private Map<String, Integer> getAppOpModesForOpName(SparseIntArray appOpCodeModes) {
147         int modesSize = appOpCodeModes.size();
148         Map<String, Integer> appOpNameModes = new ArrayMap<>(modesSize);
149 
150         for (int modeIndex = 0; modeIndex < modesSize; modeIndex++) {
151             int opCode = appOpCodeModes.keyAt(modeIndex);
152             int opMode = appOpCodeModes.valueAt(modeIndex);
153             appOpNameModes.put(AppOpsManager.opToPublicName(opCode), opMode);
154         }
155         return appOpNameModes;
156     }
157 
158     @Override
getLegacyAppOpVersion()159     public int getLegacyAppOpVersion() {
160         synchronized (mLock) {
161             if (mAppIdAppOpModes == null || mPackageAppOpModes == null) {
162                 readLegacyAppOpState();
163             }
164         }
165         return mVersionAtBoot;
166     }
167 
168     @Override
hasLegacyAppOpState()169     public boolean hasLegacyAppOpState() {
170         return getLegacyAppOpVersion() > -1;
171     }
172 }
173