1/*
2 * Copyright (C) 2016 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
17package com.android.server.om;
18
19import static android.app.AppGlobals.getPackageManager;
20import static android.content.Intent.ACTION_PACKAGE_ADDED;
21import static android.content.Intent.ACTION_PACKAGE_CHANGED;
22import static android.content.Intent.ACTION_PACKAGE_REMOVED;
23import static android.content.Intent.ACTION_USER_ADDED;
24import static android.content.Intent.ACTION_USER_REMOVED;
25import static android.content.pm.PackageManager.SIGNATURE_MATCH;
26
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.app.ActivityManager;
30import android.app.IActivityManager;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.om.IOverlayManager;
36import android.content.om.OverlayInfo;
37import android.content.pm.IPackageManager;
38import android.content.pm.PackageInfo;
39import android.content.pm.PackageManagerInternal;
40import android.content.pm.UserInfo;
41import android.net.Uri;
42import android.os.Binder;
43import android.os.Environment;
44import android.os.IBinder;
45import android.os.RemoteException;
46import android.os.ResultReceiver;
47import android.os.ShellCallback;
48import android.os.SystemProperties;
49import android.os.UserHandle;
50import android.os.UserManager;
51import android.text.TextUtils;
52import android.util.ArrayMap;
53import android.util.ArraySet;
54import android.util.AtomicFile;
55import android.util.Slog;
56import android.util.SparseArray;
57
58import com.android.internal.util.ConcurrentUtils;
59import com.android.server.FgThread;
60import com.android.server.IoThread;
61import com.android.server.LocalServices;
62import com.android.server.SystemServerInitThreadPool;
63import com.android.server.SystemService;
64import com.android.server.pm.Installer;
65import com.android.server.pm.UserManagerService;
66
67import org.xmlpull.v1.XmlPullParserException;
68
69import java.io.File;
70import java.io.FileDescriptor;
71import java.io.FileInputStream;
72import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.PrintWriter;
75import java.util.ArrayList;
76import java.util.Arrays;
77import java.util.Collections;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
81import java.util.Set;
82import java.util.concurrent.Future;
83import java.util.concurrent.atomic.AtomicBoolean;
84
85/**
86 * Service to manage asset overlays.
87 *
88 * <p>Asset overlays are additional resources that come from apks loaded
89 * alongside the system and app apks. This service, the OverlayManagerService
90 * (OMS), tracks which installed overlays to use and provides methods to change
91 * this. Changes propagate to running applications as part of the Activity
92 * lifecycle. This allows Activities to reread their resources at a well
93 * defined point.</p>
94 *
95 * <p>By itself, the OMS will not change what overlays should be active.
96 * Instead, it is only responsible for making sure that overlays *can* be used
97 * from a technical and security point of view and to activate overlays in
98 * response to external requests. The responsibility to toggle overlays on and
99 * off lies within components that implement different use-cases such as themes
100 * or dynamic customization.</p>
101 *
102 * <p>The OMS receives input from three sources:</p>
103 *
104 * <ul>
105 *     <li>Callbacks from the SystemService class, specifically when the
106 *     Android framework is booting and when the end user switches Android
107 *     users.</li>
108 *
109 *     <li>Intents from the PackageManagerService (PMS). Overlays are regular
110 *     apks, and whenever a package is installed (or removed, or has a
111 *     component enabled or disabled), the PMS broadcasts this as an intent.
112 *     When the OMS receives one of these intents, it updates its internal
113 *     representation of the available overlays and, if there was a visible
114 *     change, triggers an asset refresh in the affected apps.</li>
115 *
116 *     <li>External requests via the {@link IOverlayManager AIDL interface}.
117 *     The interface allows clients to read information about the currently
118 *     available overlays, change whether an overlay should be used or not, and
119 *     change the relative order in which overlay packages are loaded.
120 *     Read-access is granted if the request targets the same Android user as
121 *     the caller runs as, or if the caller holds the
122 *     INTERACT_ACROSS_USERS_FULL permission. Write-access is granted if the
123 *     caller is granted read-access and additionaly holds the
124 *     CHANGE_OVERLAY_PACKAGES permission.</li>
125 * </ul>
126 *
127 * <p>The AIDL interface works with String package names, int user IDs, and
128 * {@link OverlayInfo} objects. OverlayInfo instances are used to track a
129 * specific pair of target and overlay packages and include information such as
130 * the current state of the overlay. OverlayInfo objects are immutable.</p>
131 *
132 * <p>Internally, OverlayInfo objects are maintained by the
133 * OverlayManagerSettings class. The OMS and its helper classes are notified of
134 * changes to the settings by the OverlayManagerSettings.ChangeListener
135 * callback interface. The file /data/system/overlays.xml is used to persist
136 * the settings.</p>
137 *
138 * <p>Creation and deletion of idmap files are handled by the IdmapManager
139 * class.</p>
140 *
141 * <p>The following is an overview of OMS and its related classes. Note how box
142 * (2) does the heavy lifting, box (1) interacts with the Android framework,
143 * and box (3) replaces box (1) during unit testing.</p>
144 *
145 * <pre>
146 *         Android framework
147 *            |         ^
148 *      . . . | . . . . | . . . .
149 *     .      |         |       .
150 *     .    AIDL,   broadcasts  .
151 *     .   intents      |       .
152 *     .      |         |       . . . . . . . . . . . .
153 *     .      v         |       .                     .
154 *     .  OverlayManagerService . OverlayManagerTests .
155 *     .                  \     .     /               .
156 *     . (1)               \    .    /            (3) .
157 *      . . . . . . . . . . \ . . . / . . . . . . . . .
158 *     .                     \     /              .
159 *     . (2)                  \   /               .
160 *     .           OverlayManagerServiceImpl      .
161 *     .                  |            |          .
162 *     .                  |            |          .
163 *     . OverlayManagerSettings     IdmapManager  .
164 *     .                                          .
165 *     . . . .  . . . . . . . . . . . . . . . . . .
166 * </pre>
167 *
168 * <p>Finally, here is a list of keywords used in the OMS context.</p>
169 *
170 * <ul>
171 *     <li><b>target [package]</b> -- A regular apk that may have its resource
172 *     pool extended  by zero or more overlay packages.</li>
173 *
174 *     <li><b>overlay [package]</b> -- An apk that provides additional
175 *     resources to another apk.</li>
176 *
177 *     <li><b>OMS</b> -- The OverlayManagerService, i.e. this class.</li>
178 *
179 *     <li><b>approved</b> -- An overlay is approved if the OMS has verified
180 *     that it can be used technically speaking (its target package is
181 *     installed, at least one resource name in both packages match, the
182 *     idmap was created, etc) and that it is secure to do so. External
183 *     clients can not change this state.</li>
184 *
185 *     <li><b>not approved</b> -- The opposite of approved.</li>
186 *
187 *     <li><b>enabled</b> -- An overlay currently in active use and thus part
188 *     of resource lookups. This requires the overlay to be approved. Only
189 *     external clients can change this state.</li>
190 *
191 *     <li><b>disabled</b> -- The opposite of enabled.</li>
192 *
193 *     <li><b>idmap</b> -- A mapping of resource IDs between target and overlay
194 *     used during resource lookup. Also the name of the binary that creates
195 *     the mapping.</li>
196 * </ul>
197 */
198public final class OverlayManagerService extends SystemService {
199    static final String TAG = "OverlayManager";
200
201    static final boolean DEBUG = false;
202
203    /**
204     * The system property that specifies the default overlays to apply.
205     * This is a semicolon separated list of package names.
206     *
207     * Ex: com.android.vendor.overlay_one;com.android.vendor.overlay_two
208     */
209    private static final String DEFAULT_OVERLAYS_PROP = "ro.boot.vendor.overlay.theme";
210
211    private final Object mLock = new Object();
212
213    private final AtomicFile mSettingsFile;
214
215    private final PackageManagerHelper mPackageManager;
216
217    private final UserManagerService mUserManager;
218
219    private final OverlayManagerSettings mSettings;
220
221    private final OverlayManagerServiceImpl mImpl;
222
223    private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
224
225    private Future<?> mInitCompleteSignal;
226
227    public OverlayManagerService(@NonNull final Context context,
228            @NonNull final Installer installer) {
229        super(context);
230        mSettingsFile =
231            new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"));
232        mPackageManager = new PackageManagerHelper();
233        mUserManager = UserManagerService.getInstance();
234        IdmapManager im = new IdmapManager(installer);
235        mSettings = new OverlayManagerSettings();
236        mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
237                getDefaultOverlayPackages(), new OverlayChangeListener());
238        mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
239            final IntentFilter packageFilter = new IntentFilter();
240            packageFilter.addAction(ACTION_PACKAGE_ADDED);
241            packageFilter.addAction(ACTION_PACKAGE_CHANGED);
242            packageFilter.addAction(ACTION_PACKAGE_REMOVED);
243            packageFilter.addDataScheme("package");
244            getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
245                    packageFilter, null, null);
246
247            final IntentFilter userFilter = new IntentFilter();
248            userFilter.addAction(ACTION_USER_ADDED);
249            userFilter.addAction(ACTION_USER_REMOVED);
250            getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
251                    userFilter, null, null);
252
253            restoreSettings();
254
255            initIfNeeded();
256            onSwitchUser(UserHandle.USER_SYSTEM);
257
258            publishBinderService(Context.OVERLAY_SERVICE, mService);
259            publishLocalService(OverlayManagerService.class, this);
260        }, "Init OverlayManagerService");
261    }
262
263    @Override
264    public void onStart() {
265        // Intentionally left empty.
266    }
267
268    @Override
269    public void onBootPhase(int phase) {
270        if (phase == PHASE_SYSTEM_SERVICES_READY) {
271            ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
272                    "Wait for OverlayManagerService init");
273            mInitCompleteSignal = null;
274        }
275    }
276
277    private void initIfNeeded() {
278        final UserManager um = getContext().getSystemService(UserManager.class);
279        final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
280        synchronized (mLock) {
281            final int userCount = users.size();
282            for (int i = 0; i < userCount; i++) {
283                final UserInfo userInfo = users.get(i);
284                if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
285                    // Initialize any users that can't be switched to, as there state would
286                    // never be setup in onSwitchUser(). We will switch to the system user right
287                    // after this, and its state will be setup there.
288                    final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
289                    updateOverlayPaths(users.get(i).id, targets);
290                }
291            }
292        }
293    }
294
295    @Override
296    public void onSwitchUser(final int newUserId) {
297        // ensure overlays in the settings are up-to-date, and propagate
298        // any asset changes to the rest of the system
299        synchronized (mLock) {
300            final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
301            updateAssets(newUserId, targets);
302        }
303        schedulePersistSettings();
304    }
305
306    private static Set<String> getDefaultOverlayPackages() {
307        final String str = SystemProperties.get(DEFAULT_OVERLAYS_PROP);
308        if (TextUtils.isEmpty(str)) {
309            return Collections.emptySet();
310        }
311
312        final ArraySet<String> defaultPackages = new ArraySet<>();
313        for (String packageName : str.split(";")) {
314            if (!TextUtils.isEmpty(packageName)) {
315                defaultPackages.add(packageName);
316            }
317        }
318        return defaultPackages;
319    }
320
321    private final class PackageReceiver extends BroadcastReceiver {
322        @Override
323        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
324            final Uri data = intent.getData();
325            if (data == null) {
326                Slog.e(TAG, "Cannot handle package broadcast with null data");
327                return;
328            }
329            final String packageName = data.getSchemeSpecificPart();
330
331            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
332
333            final int[] userIds;
334            final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
335            if (extraUid == UserHandle.USER_NULL) {
336                userIds = mUserManager.getUserIds();
337            } else {
338                userIds = new int[] { UserHandle.getUserId(extraUid) };
339            }
340
341            switch (intent.getAction()) {
342                case ACTION_PACKAGE_ADDED:
343                    if (replacing) {
344                        onPackageUpgraded(packageName, userIds);
345                    } else {
346                        onPackageAdded(packageName, userIds);
347                    }
348                    break;
349                case ACTION_PACKAGE_CHANGED:
350                    onPackageChanged(packageName, userIds);
351                    break;
352                case ACTION_PACKAGE_REMOVED:
353                    if (replacing) {
354                        onPackageUpgrading(packageName, userIds);
355                    } else {
356                        onPackageRemoved(packageName, userIds);
357                    }
358                    break;
359                default:
360                    // do nothing
361                    break;
362            }
363        }
364
365        private void onPackageAdded(@NonNull final String packageName,
366                @NonNull final int[] userIds) {
367            for (final int userId : userIds) {
368                synchronized (mLock) {
369                    final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
370                            false);
371                    if (pi != null) {
372                        mPackageManager.cachePackageInfo(packageName, userId, pi);
373                        if (!isOverlayPackage(pi)) {
374                            mImpl.onTargetPackageAdded(packageName, userId);
375                        } else {
376                            mImpl.onOverlayPackageAdded(packageName, userId);
377                        }
378                    }
379                }
380            }
381        }
382
383        private void onPackageChanged(@NonNull final String packageName,
384                @NonNull final int[] userIds) {
385            for (int userId : userIds) {
386                synchronized (mLock) {
387                    final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
388                            false);
389                    if (pi != null) {
390                        mPackageManager.cachePackageInfo(packageName, userId, pi);
391                        if (!isOverlayPackage(pi)) {
392                            mImpl.onTargetPackageChanged(packageName, userId);
393                        } else {
394                            mImpl.onOverlayPackageChanged(packageName, userId);
395                        }
396                    }
397                }
398            }
399        }
400
401        private void onPackageUpgrading(@NonNull final String packageName,
402                @NonNull final int[] userIds) {
403            for (int userId : userIds) {
404                synchronized (mLock) {
405                    mPackageManager.forgetPackageInfo(packageName, userId);
406                    final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
407                    if (oi == null) {
408                        mImpl.onTargetPackageUpgrading(packageName, userId);
409                    } else {
410                        mImpl.onOverlayPackageUpgrading(packageName, userId);
411                    }
412                }
413            }
414        }
415
416        private void onPackageUpgraded(@NonNull final String packageName,
417                @NonNull final int[] userIds) {
418            for (int userId : userIds) {
419                synchronized (mLock) {
420                    final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
421                            false);
422                    if (pi != null) {
423                        mPackageManager.cachePackageInfo(packageName, userId, pi);
424                        if (!isOverlayPackage(pi)) {
425                            mImpl.onTargetPackageUpgraded(packageName, userId);
426                        } else {
427                            mImpl.onOverlayPackageUpgraded(packageName, userId);
428                        }
429                    }
430                }
431            }
432        }
433
434        private void onPackageRemoved(@NonNull final String packageName,
435                @NonNull final int[] userIds) {
436            for (int userId : userIds) {
437                synchronized (mLock) {
438                    mPackageManager.forgetPackageInfo(packageName, userId);
439                    final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
440                    if (oi == null) {
441                        mImpl.onTargetPackageRemoved(packageName, userId);
442                    } else {
443                        mImpl.onOverlayPackageRemoved(packageName, userId);
444                    }
445                }
446            }
447        }
448    }
449
450    private final class UserReceiver extends BroadcastReceiver {
451        @Override
452        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
453            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
454            switch (intent.getAction()) {
455                case ACTION_USER_ADDED:
456                    if (userId != UserHandle.USER_NULL) {
457                        final ArrayList<String> targets;
458                        synchronized (mLock) {
459                            targets = mImpl.updateOverlaysForUser(userId);
460                        }
461                        updateOverlayPaths(userId, targets);
462                    }
463                    break;
464
465                case ACTION_USER_REMOVED:
466                    if (userId != UserHandle.USER_NULL) {
467                        synchronized (mLock) {
468                            mImpl.onUserRemoved(userId);
469                            mPackageManager.forgetAllPackageInfos(userId);
470                        }
471                    }
472                    break;
473                default:
474                    // do nothing
475                    break;
476            }
477        }
478    }
479
480    private final IBinder mService = new IOverlayManager.Stub() {
481        @Override
482        public Map<String, List<OverlayInfo>> getAllOverlays(int userId) throws RemoteException {
483            userId = handleIncomingUser(userId, "getAllOverlays");
484
485            synchronized (mLock) {
486                return mImpl.getOverlaysForUser(userId);
487            }
488        }
489
490        @Override
491        public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName,
492                int userId) throws RemoteException {
493            userId = handleIncomingUser(userId, "getOverlayInfosForTarget");
494            if (targetPackageName == null) {
495                return Collections.emptyList();
496            }
497
498            synchronized (mLock) {
499                return mImpl.getOverlayInfosForTarget(targetPackageName, userId);
500            }
501        }
502
503        @Override
504        public OverlayInfo getOverlayInfo(@Nullable final String packageName,
505                int userId) throws RemoteException {
506            userId = handleIncomingUser(userId, "getOverlayInfo");
507            if (packageName == null) {
508                return null;
509            }
510
511            synchronized (mLock) {
512                return mImpl.getOverlayInfo(packageName, userId);
513            }
514        }
515
516        @Override
517        public boolean setEnabled(@Nullable final String packageName, final boolean enable,
518                int userId) throws RemoteException {
519            enforceChangeOverlayPackagesPermission("setEnabled");
520            userId = handleIncomingUser(userId, "setEnabled");
521            if (packageName == null) {
522                return false;
523            }
524
525            final long ident = Binder.clearCallingIdentity();
526            try {
527                synchronized (mLock) {
528                    return mImpl.setEnabled(packageName, enable, userId);
529                }
530            } finally {
531                Binder.restoreCallingIdentity(ident);
532            }
533        }
534
535        @Override
536        public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable,
537                int userId) throws RemoteException {
538            enforceChangeOverlayPackagesPermission("setEnabled");
539            userId = handleIncomingUser(userId, "setEnabled");
540            if (packageName == null || !enable) {
541                return false;
542            }
543
544            final long ident = Binder.clearCallingIdentity();
545            try {
546                synchronized (mLock) {
547                    return mImpl.setEnabledExclusive(packageName, userId);
548                }
549            } finally {
550                Binder.restoreCallingIdentity(ident);
551            }
552        }
553
554        @Override
555        public boolean setPriority(@Nullable final String packageName,
556                @Nullable final String parentPackageName, int userId) throws RemoteException {
557            enforceChangeOverlayPackagesPermission("setPriority");
558            userId = handleIncomingUser(userId, "setPriority");
559            if (packageName == null || parentPackageName == null) {
560                return false;
561            }
562
563            final long ident = Binder.clearCallingIdentity();
564            try {
565                synchronized (mLock) {
566                    return mImpl.setPriority(packageName, parentPackageName, userId);
567                }
568            } finally {
569                Binder.restoreCallingIdentity(ident);
570            }
571        }
572
573        @Override
574        public boolean setHighestPriority(@Nullable final String packageName, int userId)
575                throws RemoteException {
576            enforceChangeOverlayPackagesPermission("setHighestPriority");
577            userId = handleIncomingUser(userId, "setHighestPriority");
578            if (packageName == null) {
579                return false;
580            }
581
582            final long ident = Binder.clearCallingIdentity();
583            try {
584                synchronized (mLock) {
585                    return mImpl.setHighestPriority(packageName, userId);
586                }
587            } finally {
588                Binder.restoreCallingIdentity(ident);
589            }
590        }
591
592        @Override
593        public boolean setLowestPriority(@Nullable final String packageName, int userId)
594                throws RemoteException {
595            enforceChangeOverlayPackagesPermission("setLowestPriority");
596            userId = handleIncomingUser(userId, "setLowestPriority");
597            if (packageName == null) {
598                return false;
599            }
600
601            final long ident = Binder.clearCallingIdentity();
602            try {
603                synchronized (mLock) {
604                    return mImpl.setLowestPriority(packageName, userId);
605                }
606            } finally {
607                Binder.restoreCallingIdentity(ident);
608            }
609        }
610
611        @Override
612        public void onShellCommand(@NonNull final FileDescriptor in,
613                @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
614                @NonNull final String[] args, @NonNull final ShellCallback callback,
615                @NonNull final ResultReceiver resultReceiver) {
616            (new OverlayManagerShellCommand(this)).exec(
617                    this, in, out, err, args, callback, resultReceiver);
618        }
619
620        @Override
621        protected void dump(@NonNull final FileDescriptor fd, @NonNull final PrintWriter pw,
622                @NonNull final String[] argv) {
623            enforceDumpPermission("dump");
624
625            final boolean verbose = argv.length > 0 && "--verbose".equals(argv[0]);
626
627            synchronized (mLock) {
628                mImpl.onDump(pw);
629                mPackageManager.dump(pw, verbose);
630            }
631        }
632
633        /**
634         * Ensure that the caller has permission to interact with the given userId.
635         * If the calling user is not the same as the provided user, the caller needs
636         * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or
637         * root).
638         *
639         * @param userId the user to interact with
640         * @param message message for any SecurityException
641         */
642        private int handleIncomingUser(final int userId, @NonNull final String message) {
643            return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
644                    Binder.getCallingUid(), userId, false, true, message, null);
645        }
646
647        /**
648         * Enforce that the caller holds the CHANGE_OVERLAY_PACKAGES permission (or is
649         * system or root).
650         *
651         * @param message used as message if SecurityException is thrown
652         * @throws SecurityException if the permission check fails
653         */
654        private void enforceChangeOverlayPackagesPermission(@NonNull final String message) {
655            getContext().enforceCallingOrSelfPermission(
656                    android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, message);
657        }
658
659        /**
660         * Enforce that the caller holds the DUMP permission (or is system or root).
661         *
662         * @param message used as message if SecurityException is thrown
663         * @throws SecurityException if the permission check fails
664         */
665        private void enforceDumpPermission(@NonNull final String message) {
666            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
667                    message);
668        }
669    };
670
671    private boolean isOverlayPackage(@NonNull final PackageInfo pi) {
672        return pi != null && pi.overlayTarget != null;
673    }
674
675    private final class OverlayChangeListener
676            implements OverlayManagerServiceImpl.OverlayChangeListener {
677        @Override
678        public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
679            schedulePersistSettings();
680            FgThread.getHandler().post(() -> {
681                updateAssets(userId, targetPackageName);
682
683                final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
684                        Uri.fromParts("package", targetPackageName, null));
685                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
686
687                if (DEBUG) {
688                    Slog.d(TAG, "send broadcast " + intent);
689                }
690
691                try {
692                    ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
693                            null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
694                            userId);
695                } catch (RemoteException e) {
696                    // Intentionally left empty.
697                }
698            });
699        }
700    }
701
702    /**
703     * Updates the target packages' set of enabled overlays in PackageManager.
704     */
705    private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
706        if (DEBUG) {
707            Slog.d(TAG, "Updating overlay assets");
708        }
709        final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
710        final boolean updateFrameworkRes = targetPackageNames.contains("android");
711        if (updateFrameworkRes) {
712            targetPackageNames = pm.getTargetPackageNames(userId);
713        }
714
715        final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
716        synchronized (mLock) {
717            final List<String> frameworkOverlays =
718                mImpl.getEnabledOverlayPackageNames("android", userId);
719            final int N = targetPackageNames.size();
720            for (int i = 0; i < N; i++) {
721                final String targetPackageName = targetPackageNames.get(i);
722                List<String> list = new ArrayList<>();
723                if (!"android".equals(targetPackageName)) {
724                    list.addAll(frameworkOverlays);
725                }
726                list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
727                pendingChanges.put(targetPackageName, list);
728            }
729        }
730
731        final int N = targetPackageNames.size();
732        for (int i = 0; i < N; i++) {
733            final String targetPackageName = targetPackageNames.get(i);
734            if (DEBUG) {
735                Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
736                        + TextUtils.join(",", pendingChanges.get(targetPackageName))
737                        + "] userId=" + userId);
738            }
739
740            if (!pm.setEnabledOverlayPackages(
741                    userId, targetPackageName, pendingChanges.get(targetPackageName))) {
742                Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
743                        targetPackageName, userId));
744            }
745        }
746    }
747
748    private void updateAssets(final int userId, final String targetPackageName) {
749        updateAssets(userId, Collections.singletonList(targetPackageName));
750    }
751
752    private void updateAssets(final int userId, List<String> targetPackageNames) {
753        updateOverlayPaths(userId, targetPackageNames);
754        final IActivityManager am = ActivityManager.getService();
755        try {
756            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
757        } catch (RemoteException e) {
758            // Intentionally left empty.
759        }
760    }
761
762    private void schedulePersistSettings() {
763        if (mPersistSettingsScheduled.getAndSet(true)) {
764            return;
765        }
766        IoThread.getHandler().post(() -> {
767            mPersistSettingsScheduled.set(false);
768            if (DEBUG) {
769                Slog.d(TAG, "Writing overlay settings");
770            }
771            synchronized (mLock) {
772                FileOutputStream stream = null;
773                try {
774                    stream = mSettingsFile.startWrite();
775                    mSettings.persist(stream);
776                    mSettingsFile.finishWrite(stream);
777                } catch (IOException | XmlPullParserException e) {
778                    mSettingsFile.failWrite(stream);
779                    Slog.e(TAG, "failed to persist overlay state", e);
780                }
781            }
782        });
783    }
784
785    private void restoreSettings() {
786        synchronized (mLock) {
787            if (!mSettingsFile.getBaseFile().exists()) {
788                return;
789            }
790            try (final FileInputStream stream = mSettingsFile.openRead()) {
791                mSettings.restore(stream);
792
793                // We might have data for dying users if the device was
794                // restarted before we received USER_REMOVED. Remove data for
795                // users that will not exist after the system is ready.
796
797                final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
798                final int[] liveUserIds = new int[liveUsers.size()];
799                for (int i = 0; i < liveUsers.size(); i++) {
800                    liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
801                }
802                Arrays.sort(liveUserIds);
803
804                for (int userId : mSettings.getUsers()) {
805                    if (Arrays.binarySearch(liveUserIds, userId) < 0) {
806                        mSettings.removeUser(userId);
807                    }
808                }
809            } catch (IOException | XmlPullParserException e) {
810                Slog.e(TAG, "failed to restore overlay state", e);
811            }
812        }
813    }
814
815    private static final class PackageManagerHelper implements
816            OverlayManagerServiceImpl.PackageManagerHelper {
817
818        private final IPackageManager mPackageManager;
819        private final PackageManagerInternal mPackageManagerInternal;
820
821        // Use a cache for performance and for consistency within OMS: because
822        // additional PACKAGE_* intents may be delivered while we process an
823        // intent, querying the PackageManagerService for the actual current
824        // state may lead to contradictions within OMS. Better then to lag
825        // behind until all pending intents have been processed.
826        private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
827
828        PackageManagerHelper() {
829            mPackageManager = getPackageManager();
830            mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
831        }
832
833        public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
834                final boolean useCache) {
835            if (useCache) {
836                final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId);
837                if (cachedPi != null) {
838                    return cachedPi;
839                }
840            }
841            try {
842                final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId);
843                if (useCache && pi != null) {
844                    cachePackageInfo(packageName, userId, pi);
845                }
846                return pi;
847            } catch (RemoteException e) {
848                // Intentionally left empty.
849            }
850            return null;
851        }
852
853        @Override
854        public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
855            return getPackageInfo(packageName, userId, true);
856        }
857
858        @Override
859        public boolean signaturesMatching(@NonNull final String packageName1,
860                @NonNull final String packageName2, final int userId) {
861            // The package manager does not support different versions of packages
862            // to be installed for different users: ignore userId for now.
863            try {
864                return mPackageManager.checkSignatures(
865                        packageName1, packageName2) == SIGNATURE_MATCH;
866            } catch (RemoteException e) {
867                // Intentionally left blank
868            }
869            return false;
870        }
871
872        @Override
873        public List<PackageInfo> getOverlayPackages(final int userId) {
874            return mPackageManagerInternal.getOverlayPackages(userId);
875        }
876
877        public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
878                final int userId) {
879            final HashMap<String, PackageInfo> map = mCache.get(userId);
880            return map == null ? null : map.get(packageName);
881        }
882
883        public void cachePackageInfo(@NonNull final String packageName, final int userId,
884                @NonNull final PackageInfo pi) {
885            HashMap<String, PackageInfo> map = mCache.get(userId);
886            if (map == null) {
887                map = new HashMap<>();
888                mCache.put(userId, map);
889            }
890            map.put(packageName, pi);
891        }
892
893        public void forgetPackageInfo(@NonNull final String packageName, final int userId) {
894            final HashMap<String, PackageInfo> map = mCache.get(userId);
895            if (map == null) {
896                return;
897            }
898            map.remove(packageName);
899            if (map.isEmpty()) {
900                mCache.delete(userId);
901            }
902        }
903
904        public void forgetAllPackageInfos(final int userId) {
905            mCache.delete(userId);
906        }
907
908        private static final String TAB1 = "    ";
909        private static final String TAB2 = TAB1 + TAB1;
910
911        public void dump(@NonNull final PrintWriter pw, final boolean verbose) {
912            pw.println("PackageInfo cache");
913
914            if (!verbose) {
915                int count = 0;
916                final int N = mCache.size();
917                for (int i = 0; i < N; i++) {
918                    final int userId = mCache.keyAt(i);
919                    count += mCache.get(userId).size();
920                }
921                pw.println(TAB1 + count + " package(s)");
922                return;
923            }
924
925            if (mCache.size() == 0) {
926                pw.println(TAB1 + "<empty>");
927                return;
928            }
929
930            final int N = mCache.size();
931            for (int i = 0; i < N; i++) {
932                final int userId = mCache.keyAt(i);
933                pw.println(TAB1 + "User " + userId);
934                final HashMap<String, PackageInfo> map = mCache.get(userId);
935                for (Map.Entry<String, PackageInfo> entry : map.entrySet()) {
936                    pw.println(TAB2 + entry.getKey() + ": " + entry.getValue());
937                }
938            }
939        }
940    }
941}
942