1 /*
2  * Copyright (C) 2022 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.pm.local;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.UserIdInt;
23 import android.os.Binder;
24 import android.os.UserHandle;
25 import android.util.ArrayMap;
26 
27 import com.android.server.pm.Computer;
28 import com.android.server.pm.PackageManagerLocal;
29 import com.android.server.pm.PackageManagerService;
30 import com.android.server.pm.pkg.PackageState;
31 import com.android.server.pm.snapshot.PackageDataSnapshot;
32 
33 import java.io.IOException;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Map;
37 
38 /** @hide */
39 public class PackageManagerLocalImpl implements PackageManagerLocal {
40 
41     private final PackageManagerService mService;
42 
PackageManagerLocalImpl(PackageManagerService service)43     public PackageManagerLocalImpl(PackageManagerService service) {
44         mService = service;
45     }
46 
47     @Override
reconcileSdkData(@ullable String volumeUuid, @NonNull String packageName, @NonNull List<String> subDirNames, int userId, int appId, int previousAppId, @NonNull String seInfo, int flags)48     public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
49             @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
50             @NonNull String seInfo, int flags) throws IOException {
51         mService.reconcileSdkData(volumeUuid, packageName, subDirNames, userId, appId,
52                 previousAppId, seInfo, flags);
53     }
54 
55     @NonNull
56     @Override
withUnfilteredSnapshot()57     public UnfilteredSnapshotImpl withUnfilteredSnapshot() {
58         return new UnfilteredSnapshotImpl(mService.snapshotComputer(false /*allowLiveComputer*/));
59     }
60 
61     @NonNull
62     @Override
withFilteredSnapshot()63     public FilteredSnapshotImpl withFilteredSnapshot() {
64         return withFilteredSnapshot(Binder.getCallingUid(), Binder.getCallingUserHandle());
65     }
66 
67     @NonNull
68     @Override
withFilteredSnapshot(int callingUid, @NonNull UserHandle user)69     public FilteredSnapshotImpl withFilteredSnapshot(int callingUid, @NonNull UserHandle user) {
70         return new FilteredSnapshotImpl(callingUid, user,
71                 mService.snapshotComputer(false /*allowLiveComputer*/), null);
72     }
73 
74     private abstract static class BaseSnapshotImpl implements AutoCloseable {
75 
76         private boolean mClosed;
77 
78         @NonNull
79         protected Computer mSnapshot;
80 
BaseSnapshotImpl(@onNull PackageDataSnapshot snapshot)81         private BaseSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
82             mSnapshot = (Computer) snapshot;
83         }
84 
85         @CallSuper
86         @Override
close()87         public void close() {
88             mClosed = true;
89             mSnapshot = null;
90             // TODO: Recycle snapshots?
91         }
92 
93         @CallSuper
checkClosed()94         protected void checkClosed() {
95             if (mClosed) {
96                 throw new IllegalStateException("Snapshot already closed");
97             }
98         }
99     }
100 
101     private static class UnfilteredSnapshotImpl extends BaseSnapshotImpl implements
102             UnfilteredSnapshot {
103 
104         @Nullable
105         private Map<String, PackageState> mCachedUnmodifiablePackageStates;
106 
107         @Nullable
108         private Map<String, PackageState> mCachedUnmodifiableDisabledSystemPackageStates;
109 
UnfilteredSnapshotImpl(@onNull PackageDataSnapshot snapshot)110         private UnfilteredSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
111             super(snapshot);
112         }
113 
114         @Override
filtered(int callingUid, @NonNull UserHandle user)115         public FilteredSnapshot filtered(int callingUid, @NonNull UserHandle user) {
116             return new FilteredSnapshotImpl(callingUid, user, mSnapshot, this);
117         }
118 
119         @SuppressWarnings("RedundantSuppression")
120         @NonNull
121         @Override
getPackageStates()122         public Map<String, PackageState> getPackageStates() {
123             checkClosed();
124 
125             if (mCachedUnmodifiablePackageStates == null) {
126                 mCachedUnmodifiablePackageStates =
127                         Collections.unmodifiableMap(mSnapshot.getPackageStates());
128             }
129             return mCachedUnmodifiablePackageStates;
130         }
131 
132         @SuppressWarnings("RedundantSuppression")
133         @NonNull
134         @Override
getDisabledSystemPackageStates()135         public Map<String, PackageState> getDisabledSystemPackageStates() {
136             checkClosed();
137 
138             if (mCachedUnmodifiableDisabledSystemPackageStates == null) {
139                 mCachedUnmodifiableDisabledSystemPackageStates =
140                         Collections.unmodifiableMap(mSnapshot.getDisabledSystemPackageStates());
141             }
142             return mCachedUnmodifiableDisabledSystemPackageStates;
143         }
144 
145         @Override
close()146         public void close() {
147             super.close();
148             mCachedUnmodifiablePackageStates = null;
149             mCachedUnmodifiableDisabledSystemPackageStates = null;
150         }
151     }
152 
153     private static class FilteredSnapshotImpl extends BaseSnapshotImpl implements
154             FilteredSnapshot {
155 
156         private final int mCallingUid;
157 
158         @UserIdInt
159         private final int mUserId;
160 
161         @Nullable
162         private Map<String, PackageState> mFilteredPackageStates;
163 
164         @Nullable
165         private final UnfilteredSnapshotImpl mParentSnapshot;
166 
FilteredSnapshotImpl(int callingUid, @NonNull UserHandle user, @NonNull PackageDataSnapshot snapshot, @Nullable UnfilteredSnapshotImpl parentSnapshot)167         private FilteredSnapshotImpl(int callingUid, @NonNull UserHandle user,
168                 @NonNull PackageDataSnapshot snapshot,
169                 @Nullable UnfilteredSnapshotImpl parentSnapshot) {
170             super(snapshot);
171             mCallingUid = callingUid;
172             mUserId = user.getIdentifier();
173             mParentSnapshot = parentSnapshot;
174         }
175 
176         @Override
checkClosed()177         protected void checkClosed() {
178             if (mParentSnapshot != null) {
179                 mParentSnapshot.checkClosed();
180             }
181 
182             super.checkClosed();
183         }
184 
185         @Override
close()186         public void close() {
187             super.close();
188             mFilteredPackageStates = null;
189         }
190 
191         @Nullable
192         @Override
getPackageState(@onNull String packageName)193         public PackageState getPackageState(@NonNull String packageName) {
194             checkClosed();
195             return mSnapshot.getPackageStateFiltered(packageName, mCallingUid, mUserId);
196         }
197 
198         @NonNull
199         @Override
getPackageStates()200         public Map<String, PackageState> getPackageStates() {
201             checkClosed();
202 
203             if (mFilteredPackageStates == null) {
204                 var packageStates = mSnapshot.getPackageStates();
205                 var filteredPackageStates = new ArrayMap<String, PackageState>();
206                 for (int index = 0, size = packageStates.size(); index < size; index++) {
207                     var packageState = packageStates.valueAt(index);
208                     if (!mSnapshot.shouldFilterApplication(packageState, mCallingUid, mUserId)) {
209                         filteredPackageStates.put(packageStates.keyAt(index), packageState);
210                     }
211                 }
212                 mFilteredPackageStates = Collections.unmodifiableMap(filteredPackageStates);
213             }
214 
215             return mFilteredPackageStates;
216         }
217     }
218 }
219