1 /*
2  * Copyright (C) 2021 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.pkg.mutate;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ComponentName;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.overlay.OverlayPaths;
25 import android.util.ArraySet;
26 
27 import com.android.server.pm.PackageSetting;
28 import com.android.server.pm.pkg.PackageUserStateImpl;
29 import com.android.server.pm.pkg.SuspendParams;
30 
31 import java.util.concurrent.atomic.AtomicLong;
32 import java.util.function.Function;
33 
34 public class PackageStateMutator {
35 
36     private static final AtomicLong sStateChangeSequence = new AtomicLong();
37 
38     private final StateWriteWrapper mStateWrite = new StateWriteWrapper();
39 
40     private final Function<String, PackageSetting> mActiveStateFunction;
41     private final Function<String, PackageSetting> mDisabledStateFunction;
42 
43     private final ArraySet<PackageSetting> mChangedStates = new ArraySet<>();
44 
PackageStateMutator(@onNull Function<String, PackageSetting> activeStateFunction, @NonNull Function<String, PackageSetting> disabledStateFunction)45     public PackageStateMutator(@NonNull Function<String, PackageSetting> activeStateFunction,
46             @NonNull Function<String, PackageSetting> disabledStateFunction) {
47         mActiveStateFunction = activeStateFunction;
48         mDisabledStateFunction = disabledStateFunction;
49     }
50 
onPackageStateChanged()51     public static void onPackageStateChanged() {
52         sStateChangeSequence.incrementAndGet();
53     }
54 
55     @NonNull
forPackage(@onNull String packageName)56     public PackageStateWrite forPackage(@NonNull String packageName) {
57         return setState(mActiveStateFunction.apply(packageName));
58     }
59 
60     @Nullable
forPackageNullable(@onNull String packageName)61     public PackageStateWrite forPackageNullable(@NonNull String packageName) {
62         final PackageSetting packageState = mActiveStateFunction.apply(packageName);
63         setState(packageState);
64         if (packageState == null) {
65             return null;
66         }
67 
68         return setState(packageState);
69     }
70 
71     @NonNull
forDisabledSystemPackage(@onNull String packageName)72     public PackageStateWrite forDisabledSystemPackage(@NonNull String packageName) {
73         return setState(mDisabledStateFunction.apply(packageName));
74     }
75 
76     @Nullable
forDisabledSystemPackageNullable(@onNull String packageName)77     public PackageStateWrite forDisabledSystemPackageNullable(@NonNull String packageName) {
78         final PackageSetting packageState = mDisabledStateFunction.apply(packageName);
79         if (packageState == null) {
80             return null;
81         }
82 
83         return setState(packageState);
84     }
85 
86     @NonNull
initialState(int changedPackagesSequenceNumber)87     public InitialState initialState(int changedPackagesSequenceNumber) {
88         return new InitialState(changedPackagesSequenceNumber, sStateChangeSequence.get());
89     }
90 
91     /**
92      * @return null if initial state is null or if nothing has changed, otherwise return result
93      * with what changed
94      */
95     @Nullable
generateResult(@ullable InitialState state, int changedPackagesSequenceNumber)96     public Result generateResult(@Nullable InitialState state, int changedPackagesSequenceNumber) {
97         if (state == null) {
98             return Result.SUCCESS;
99         }
100 
101         boolean packagesChanged = changedPackagesSequenceNumber != state.mPackageSequence;
102         boolean stateChanged = sStateChangeSequence.get() != state.mStateSequence;
103         if (packagesChanged && stateChanged) {
104             return Result.PACKAGES_AND_STATE_CHANGED;
105         } else if (packagesChanged) {
106             return Result.PACKAGES_CHANGED;
107         } else if (stateChanged) {
108             return Result.STATE_CHANGED;
109         } else {
110             return Result.SUCCESS;
111         }
112     }
113 
onFinished()114     public void onFinished() {
115         for (int index = 0; index < mChangedStates.size(); index++) {
116             mChangedStates.valueAt(index).onChanged();
117         }
118     }
119 
120     @NonNull
setState(@ullable PackageSetting state)121     private StateWriteWrapper setState(@Nullable PackageSetting state) {
122         // State can be nullable because this infrastructure no-ops on non-existent states
123         if (state != null) {
124             mChangedStates.add(state);
125         }
126         return mStateWrite.setState(state);
127     }
128 
129     public static class InitialState {
130 
131         private final int mPackageSequence;
132         private final long mStateSequence;
133 
InitialState(int packageSequence, long stateSequence)134         public InitialState(int packageSequence, long stateSequence) {
135             mPackageSequence = packageSequence;
136             mStateSequence = stateSequence;
137         }
138     }
139 
140     public static class Result {
141 
142         public static final Result SUCCESS = new Result(true, false, false, false);
143         public static final Result PACKAGES_CHANGED = new Result(false, true, false, false);
144         public static final Result STATE_CHANGED = new Result(false, false, true, false);
145         public static final Result PACKAGES_AND_STATE_CHANGED = new Result(false, true, true, false);
146         public static final Result SPECIFIC_PACKAGE_NULL = new Result(false, false, true, true);
147 
148         private final boolean mCommitted;
149         private final boolean mPackagesChanged;
150         private final boolean mStateChanged;
151         private final boolean mSpecificPackageNull;
152 
Result(boolean committed, boolean packagesChanged, boolean stateChanged, boolean specificPackageNull)153         public Result(boolean committed, boolean packagesChanged, boolean stateChanged,
154                 boolean specificPackageNull) {
155             mCommitted = committed;
156             mPackagesChanged = packagesChanged;
157             mStateChanged = stateChanged;
158             mSpecificPackageNull = specificPackageNull;
159         }
160 
isCommitted()161         public boolean isCommitted() {
162             return mCommitted;
163         }
164 
isPackagesChanged()165         public boolean isPackagesChanged() {
166             return mPackagesChanged;
167         }
168 
isStateChanged()169         public boolean isStateChanged() {
170             return mStateChanged;
171         }
172 
isSpecificPackageNull()173         public boolean isSpecificPackageNull() {
174             return mSpecificPackageNull;
175         }
176     }
177 
178     private static class StateWriteWrapper implements PackageStateWrite {
179 
180         private final UserStateWriteWrapper mUserStateWrite = new UserStateWriteWrapper();
181 
182         @NonNull
183         private PackageSetting mState;
184 
setState(PackageSetting state)185         public StateWriteWrapper setState(PackageSetting state) {
186             this.mState = state;
187             return this;
188         }
189 
190         @NonNull
191         @Override
userState(int userId)192         public PackageUserStateWrite userState(int userId) {
193             var userState = mState == null ? null : mState.getOrCreateUserState(userId);
194             if (userState != null) {
195                 userState.setWatchable(mState);
196             }
197             return mUserStateWrite.setStates(userState);
198         }
199 
200         @Override
onChanged()201         public void onChanged() {
202             if (mState != null) {
203                 mState.onChanged();
204             }
205         }
206 
207         @Override
setLastPackageUsageTime(int reason, long timeInMillis)208         public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
209             if (mState != null) {
210                 mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
211             }
212             return this;
213         }
214 
215         @Override
setHiddenUntilInstalled(boolean value)216         public PackageStateWrite setHiddenUntilInstalled(boolean value) {
217             if (mState != null) {
218                 mState.getTransientState().setHiddenUntilInstalled(value);
219             }
220             return this;
221         }
222 
223         @NonNull
224         @Override
setRequiredForSystemUser(boolean requiredForSystemUser)225         public PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser) {
226             if (mState != null) {
227                 if (requiredForSystemUser) {
228                     mState.setPrivateFlags(mState.getPrivateFlags()
229                             | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
230                 } else {
231                     mState.setPrivateFlags(mState.getPrivateFlags()
232                             & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
233                 }
234             }
235             return this;
236         }
237 
238         @NonNull
239         @Override
setMimeGroup(@onNull String mimeGroup, @NonNull ArraySet<String> mimeTypes)240         public PackageStateWrite setMimeGroup(@NonNull String mimeGroup,
241                 @NonNull ArraySet<String> mimeTypes) {
242             if (mState != null) {
243                 mState.setMimeGroup(mimeGroup, mimeTypes);
244             }
245             return this;
246         }
247 
248         @NonNull
249         @Override
setCategoryOverride(@pplicationInfo.Category int category)250         public PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category) {
251             if (mState != null) {
252                 mState.setCategoryOverride(category);
253             }
254             return this;
255         }
256 
257         @NonNull
258         @Override
setUpdateAvailable(boolean updateAvailable)259         public PackageStateWrite setUpdateAvailable(boolean updateAvailable) {
260             if (mState != null) {
261                 mState.setUpdateAvailable(updateAvailable);
262             }
263             return this;
264         }
265 
266         @NonNull
267         @Override
setLoadingProgress(float progress)268         public PackageStateWrite setLoadingProgress(float progress) {
269             if (mState != null) {
270                 mState.setLoadingProgress(progress);
271             }
272             return this;
273         }
274 
275         @NonNull
276         @Override
setLoadingCompletedTime(long loadingCompletedTime)277         public PackageStateWrite setLoadingCompletedTime(long loadingCompletedTime) {
278             if (mState != null) {
279                 mState.setLoadingCompletedTime(loadingCompletedTime);
280             }
281             return this;
282         }
283 
284         @NonNull
285         @Override
setOverrideSeInfo(@ullable String newSeInfo)286         public PackageStateWrite setOverrideSeInfo(@Nullable String newSeInfo) {
287             if (mState != null) {
288                 mState.getTransientState().setOverrideSeInfo(newSeInfo);
289             }
290             return this;
291         }
292 
293         @NonNull
294         @Override
setInstaller(@ullable String installerPackageName, int installerPackageUid)295         public PackageStateWrite setInstaller(@Nullable String installerPackageName,
296                 int installerPackageUid) {
297             if (mState != null) {
298                 mState.setInstallerPackage(installerPackageName, installerPackageUid);
299             }
300             return this;
301         }
302 
303         @NonNull
304         @Override
setUpdateOwner(@onNull String updateOwnerPackageName)305         public PackageStateWrite setUpdateOwner(@NonNull String updateOwnerPackageName) {
306             if (mState != null) {
307                 mState.setUpdateOwnerPackage(updateOwnerPackageName);
308             }
309             return this;
310         }
311 
312         private static class UserStateWriteWrapper implements PackageUserStateWrite {
313 
314             @Nullable
315             private PackageUserStateImpl mUserState;
316 
setStates(@ullable PackageUserStateImpl userState)317             public UserStateWriteWrapper setStates(@Nullable PackageUserStateImpl userState) {
318                 mUserState = userState;
319                 return this;
320             }
321 
322             @NonNull
323             @Override
setInstalled(boolean installed)324             public PackageUserStateWrite setInstalled(boolean installed) {
325                 if (mUserState != null) {
326                     mUserState.setInstalled(installed);
327                 }
328                 return this;
329             }
330 
331             @NonNull
332             @Override
setUninstallReason(int reason)333             public PackageUserStateWrite setUninstallReason(int reason) {
334                 if (mUserState != null) {
335                     mUserState.setUninstallReason(reason);
336                 }
337                 return this;
338             }
339 
340             @NonNull
341             @Override
setDistractionFlags( @ackageManager.DistractionRestriction int restrictionFlags)342             public PackageUserStateWrite setDistractionFlags(
343                     @PackageManager.DistractionRestriction int restrictionFlags) {
344                 if (mUserState != null) {
345                     mUserState.setDistractionFlags(restrictionFlags);
346                 }
347                 return this;
348             }
349 
350             @NonNull
351             @Override
putSuspendParams(@onNull String suspendingPackage, @Nullable SuspendParams suspendParams)352             public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
353                     @Nullable SuspendParams suspendParams) {
354                 if (mUserState != null) {
355                     mUserState.putSuspendParams(suspendingPackage, suspendParams);
356                 }
357                 return this;
358             }
359 
360             @NonNull
361             @Override
removeSuspension(@onNull String suspendingPackage)362             public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
363                 if (mUserState != null) {
364                     mUserState.removeSuspension(suspendingPackage);
365                 }
366                 return this;
367             }
368 
369             @NonNull
370             @Override
setHidden(boolean hidden)371             public PackageUserStateWrite setHidden(boolean hidden) {
372                 if (mUserState != null) {
373                     mUserState.setHidden(hidden);
374                 }
375                 return this;
376             }
377 
378             @NonNull
379             @Override
setStopped(boolean stopped)380             public PackageUserStateWrite setStopped(boolean stopped) {
381                 if (mUserState != null) {
382                     mUserState.setStopped(stopped);
383                 }
384                 return this;
385             }
386 
387             @NonNull
388             @Override
setNotLaunched(boolean notLaunched)389             public PackageUserStateWrite setNotLaunched(boolean notLaunched) {
390                 if (mUserState != null) {
391                     mUserState.setNotLaunched(notLaunched);
392                 }
393                 return this;
394             }
395 
396             @NonNull
397             @Override
setOverlayPaths(@onNull OverlayPaths overlayPaths)398             public PackageUserStateWrite setOverlayPaths(@NonNull OverlayPaths overlayPaths) {
399                 if (mUserState != null) {
400                     mUserState.setOverlayPaths(overlayPaths);
401                 }
402                 return this;
403             }
404 
405             @NonNull
406             @Override
setOverlayPathsForLibrary(@onNull String libraryName, @Nullable OverlayPaths overlayPaths)407             public PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
408                     @Nullable OverlayPaths overlayPaths) {
409                 if (mUserState != null) {
410                     mUserState.setSharedLibraryOverlayPaths(libraryName, overlayPaths);
411                 }
412                 return this;
413             }
414 
415             @NonNull
416             @Override
setHarmfulAppWarning(@ullable String warning)417             public PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning) {
418                 if (mUserState != null) {
419                     mUserState.setHarmfulAppWarning(warning);
420                 }
421                 return this;
422             }
423 
424             @NonNull
425             @Override
setSplashScreenTheme(@ullable String theme)426             public PackageUserStateWrite setSplashScreenTheme(@Nullable String theme) {
427                 if (mUserState != null) {
428                     mUserState.setSplashScreenTheme(theme);
429                 }
430                 return this;
431             }
432 
433             @NonNull
434             @Override
setComponentLabelIcon(@onNull ComponentName componentName, @Nullable String nonLocalizedLabel, @Nullable Integer icon)435             public PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
436                     @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
437                 if (mUserState != null) {
438                     mUserState.overrideLabelAndIcon(componentName, nonLocalizedLabel, icon);
439                 }
440                 return null;
441             }
442 
443             @NonNull
444             @Override
setMinAspectRatio( @ackageManager.UserMinAspectRatio int aspectRatio)445             public PackageUserStateWrite setMinAspectRatio(
446                     @PackageManager.UserMinAspectRatio int aspectRatio) {
447                 if (mUserState != null) {
448                     mUserState.setMinAspectRatio(aspectRatio);
449                 }
450                 return this;
451             }
452         }
453     }
454 }
455