Home | History | Annotate | Download | only in slice
      1 /*
      2  * Copyright (C) 2017 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.slice;
     18 
     19 import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
     20 import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
     21 import static android.content.ContentProvider.getUriWithoutUserId;
     22 import static android.content.ContentProvider.getUserIdFromUri;
     23 import static android.content.ContentProvider.maybeAddUserId;
     24 import static android.content.pm.PackageManager.PERMISSION_DENIED;
     25 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
     26 import static android.os.Process.SYSTEM_UID;
     27 
     28 import android.Manifest.permission;
     29 import android.app.ActivityManager;
     30 import android.app.AppOpsManager;
     31 import android.app.ContentProviderHolder;
     32 import android.app.IActivityManager;
     33 import android.app.slice.ISliceManager;
     34 import android.app.slice.SliceSpec;
     35 import android.app.usage.UsageStatsManagerInternal;
     36 import android.content.BroadcastReceiver;
     37 import android.content.ComponentName;
     38 import android.content.ContentProvider;
     39 import android.content.ContentResolver;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.PackageManager;
     44 import android.content.pm.PackageManagerInternal;
     45 import android.content.pm.ResolveInfo;
     46 import android.net.Uri;
     47 import android.os.Binder;
     48 import android.os.Handler;
     49 import android.os.IBinder;
     50 import android.os.Looper;
     51 import android.os.Process;
     52 import android.os.RemoteException;
     53 import android.os.ResultReceiver;
     54 import android.os.ShellCallback;
     55 import android.os.UserHandle;
     56 import android.util.ArrayMap;
     57 import android.util.Slog;
     58 import android.util.SparseArray;
     59 import android.util.Xml.Encoding;
     60 
     61 import com.android.internal.annotations.GuardedBy;
     62 import com.android.internal.annotations.VisibleForTesting;
     63 import com.android.internal.app.AssistUtils;
     64 import com.android.internal.util.Preconditions;
     65 import com.android.server.LocalServices;
     66 import com.android.server.ServiceThread;
     67 import com.android.server.SystemService;
     68 
     69 import org.xmlpull.v1.XmlPullParser;
     70 import org.xmlpull.v1.XmlPullParserException;
     71 import org.xmlpull.v1.XmlPullParserFactory;
     72 import org.xmlpull.v1.XmlSerializer;
     73 
     74 import java.io.ByteArrayInputStream;
     75 import java.io.ByteArrayOutputStream;
     76 import java.io.FileDescriptor;
     77 import java.io.IOException;
     78 import java.util.ArrayList;
     79 import java.util.List;
     80 import java.util.Objects;
     81 import java.util.function.Supplier;
     82 
     83 public class SliceManagerService extends ISliceManager.Stub {
     84 
     85     private static final String TAG = "SliceManagerService";
     86     private final Object mLock = new Object();
     87 
     88     private final Context mContext;
     89     private final PackageManagerInternal mPackageManagerInternal;
     90     private final AppOpsManager mAppOps;
     91     private final AssistUtils mAssistUtils;
     92 
     93     @GuardedBy("mLock")
     94     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
     95     @GuardedBy("mLock")
     96     private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>();
     97     @GuardedBy("mLock")
     98     private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>();
     99     private final Handler mHandler;
    100 
    101     private final SlicePermissionManager mPermissions;
    102     private final UsageStatsManagerInternal mAppUsageStats;
    103 
    104     public SliceManagerService(Context context) {
    105         this(context, createHandler().getLooper());
    106     }
    107 
    108     @VisibleForTesting
    109     SliceManagerService(Context context, Looper looper) {
    110         mContext = context;
    111         mPackageManagerInternal = Preconditions.checkNotNull(
    112                 LocalServices.getService(PackageManagerInternal.class));
    113         mAppOps = context.getSystemService(AppOpsManager.class);
    114         mAssistUtils = new AssistUtils(context);
    115         mHandler = new Handler(looper);
    116 
    117         mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
    118 
    119         mPermissions = new SlicePermissionManager(mContext, looper);
    120 
    121         IntentFilter filter = new IntentFilter();
    122         filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
    123         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    124         filter.addDataScheme("package");
    125         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
    126     }
    127 
    128     ///  ----- Lifecycle stuff -----
    129     private void systemReady() {
    130     }
    131 
    132     private void onUnlockUser(int userId) {
    133     }
    134 
    135     private void onStopUser(int userId) {
    136         synchronized (mLock) {
    137             mPinnedSlicesByUri.values().removeIf(s -> getUserIdFromUri(s.getUri()) == userId);
    138         }
    139     }
    140 
    141     ///  ----- ISliceManager stuff -----
    142     @Override
    143     public Uri[] getPinnedSlices(String pkg) {
    144         verifyCaller(pkg);
    145         int callingUser = Binder.getCallingUserHandle().getIdentifier();
    146         ArrayList<Uri> ret = new ArrayList<>();
    147         synchronized (mLock) {
    148             for (PinnedSliceState state : mPinnedSlicesByUri.values()) {
    149                 if (Objects.equals(pkg, state.getPkg())) {
    150                     Uri uri = state.getUri();
    151                     int userId = ContentProvider.getUserIdFromUri(uri, callingUser);
    152                     if (userId == callingUser) {
    153                         ret.add(ContentProvider.getUriWithoutUserId(uri));
    154                     }
    155                 }
    156             }
    157         }
    158         return ret.toArray(new Uri[ret.size()]);
    159     }
    160 
    161     @Override
    162     public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token)
    163             throws RemoteException {
    164         verifyCaller(pkg);
    165         enforceAccess(pkg, uri);
    166         int user = Binder.getCallingUserHandle().getIdentifier();
    167         uri = maybeAddUserId(uri, user);
    168         String slicePkg = getProviderPkg(uri, user);
    169         getOrCreatePinnedSlice(uri, slicePkg).pin(pkg, specs, token);
    170 
    171         mHandler.post(() -> {
    172             if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
    173                 mAppUsageStats.reportEvent(slicePkg, user,
    174                         isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
    175                                 ? SLICE_PINNED_PRIV : SLICE_PINNED);
    176             }
    177         });
    178     }
    179 
    180     @Override
    181     public void unpinSlice(String pkg, Uri uri, IBinder token) throws RemoteException {
    182         verifyCaller(pkg);
    183         enforceAccess(pkg, uri);
    184         uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
    185         if (getPinnedSlice(uri).unpin(pkg, token)) {
    186             removePinnedSlice(uri);
    187         }
    188     }
    189 
    190     @Override
    191     public boolean hasSliceAccess(String pkg) throws RemoteException {
    192         verifyCaller(pkg);
    193         return hasFullSliceAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
    194     }
    195 
    196     @Override
    197     public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
    198         verifyCaller(pkg);
    199         enforceAccess(pkg, uri);
    200         return getPinnedSlice(uri).getSpecs();
    201     }
    202 
    203     @Override
    204     public void grantSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
    205         verifyCaller(pkg);
    206         int user = Binder.getCallingUserHandle().getIdentifier();
    207         enforceOwner(pkg, uri, user);
    208         mPermissions.grantSliceAccess(toPkg, user, pkg, user, uri);
    209     }
    210 
    211     @Override
    212     public void revokeSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
    213         verifyCaller(pkg);
    214         int user = Binder.getCallingUserHandle().getIdentifier();
    215         enforceOwner(pkg, uri, user);
    216         mPermissions.revokeSliceAccess(toPkg, user, pkg, user, uri);
    217     }
    218 
    219     @Override
    220     public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
    221             String[] autoGrantPermissions) {
    222         int userId = UserHandle.getUserId(uid);
    223         if (pkg == null) {
    224             for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
    225                 if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
    226                         == PERMISSION_GRANTED) {
    227                     return PERMISSION_GRANTED;
    228                 }
    229             }
    230             return PERMISSION_DENIED;
    231         }
    232         if (hasFullSliceAccess(pkg, userId)) {
    233             return PackageManager.PERMISSION_GRANTED;
    234         }
    235         if (mPermissions.hasPermission(pkg, userId, uri)) {
    236             return PackageManager.PERMISSION_GRANTED;
    237         }
    238         if (autoGrantPermissions != null) {
    239             // Need to own the Uri to call in with permissions to grant.
    240             enforceOwner(pkg, uri, userId);
    241             for (String perm : autoGrantPermissions) {
    242                 if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
    243                     int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
    244                     String providerPkg = getProviderPkg(uri, providerUser);
    245                     mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, uri);
    246                     return PackageManager.PERMISSION_GRANTED;
    247                 }
    248             }
    249         }
    250         // Fallback to allowing uri permissions through.
    251         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    252                 == PERMISSION_GRANTED) {
    253             return PackageManager.PERMISSION_GRANTED;
    254         }
    255         return PackageManager.PERMISSION_DENIED;
    256     }
    257 
    258     @Override
    259     public void grantPermissionFromUser(Uri uri, String pkg, String callingPkg, boolean allSlices) {
    260         verifyCaller(callingPkg);
    261         getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
    262                 "Slice granting requires MANAGE_SLICE_PERMISSIONS");
    263         int userId = Binder.getCallingUserHandle().getIdentifier();
    264         if (allSlices) {
    265             mPermissions.grantFullAccess(pkg, userId);
    266         } else {
    267             // When granting, grant to all slices in the provider.
    268             Uri grantUri = uri.buildUpon()
    269                     .path("")
    270                     .build();
    271             int providerUser = ContentProvider.getUserIdFromUri(grantUri, userId);
    272             String providerPkg = getProviderPkg(grantUri, providerUser);
    273             mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, grantUri);
    274         }
    275         long ident = Binder.clearCallingIdentity();
    276         try {
    277             mContext.getContentResolver().notifyChange(uri, null);
    278         } finally {
    279             Binder.restoreCallingIdentity(ident);
    280         }
    281     }
    282 
    283     // Backup/restore interface
    284     @Override
    285     public byte[] getBackupPayload(int user) {
    286         if (Binder.getCallingUid() != SYSTEM_UID) {
    287             throw new SecurityException("Caller must be system");
    288         }
    289         //TODO: http://b/22388012
    290         if (user != UserHandle.USER_SYSTEM) {
    291             Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
    292             return null;
    293         }
    294         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    295         try {
    296             XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
    297             out.setOutput(baos, Encoding.UTF_8.name());
    298 
    299             mPermissions.writeBackup(out);
    300 
    301             out.flush();
    302             return baos.toByteArray();
    303         } catch (IOException | XmlPullParserException e) {
    304             Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
    305         }
    306         return null;
    307     }
    308 
    309     @Override
    310     public void applyRestore(byte[] payload, int user) {
    311         if (Binder.getCallingUid() != SYSTEM_UID) {
    312             throw new SecurityException("Caller must be system");
    313         }
    314         if (payload == null) {
    315             Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
    316             return;
    317         }
    318         //TODO: http://b/22388012
    319         if (user != UserHandle.USER_SYSTEM) {
    320             Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
    321             return;
    322         }
    323         final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
    324         try {
    325             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
    326             parser.setInput(bais, Encoding.UTF_8.name());
    327             mPermissions.readRestore(parser);
    328         } catch (NumberFormatException | XmlPullParserException | IOException e) {
    329             Slog.w(TAG, "applyRestore: error reading payload", e);
    330         }
    331     }
    332 
    333     @Override
    334     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
    335             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
    336         new SliceShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
    337     }
    338 
    339     ///  ----- internal code -----
    340     private void enforceOwner(String pkg, Uri uri, int user) {
    341         if (!Objects.equals(getProviderPkg(uri, user), pkg) || pkg == null) {
    342             throw new SecurityException("Caller must own " + uri);
    343         }
    344     }
    345 
    346     protected void removePinnedSlice(Uri uri) {
    347         synchronized (mLock) {
    348             mPinnedSlicesByUri.remove(uri).destroy();
    349         }
    350     }
    351 
    352     private PinnedSliceState getPinnedSlice(Uri uri) {
    353         synchronized (mLock) {
    354             PinnedSliceState manager = mPinnedSlicesByUri.get(uri);
    355             if (manager == null) {
    356                 throw new IllegalStateException(String.format("Slice %s not pinned",
    357                         uri.toString()));
    358             }
    359             return manager;
    360         }
    361     }
    362 
    363     private PinnedSliceState getOrCreatePinnedSlice(Uri uri, String pkg) {
    364         synchronized (mLock) {
    365             PinnedSliceState manager = mPinnedSlicesByUri.get(uri);
    366             if (manager == null) {
    367                 manager = createPinnedSlice(uri, pkg);
    368                 mPinnedSlicesByUri.put(uri, manager);
    369             }
    370             return manager;
    371         }
    372     }
    373 
    374     @VisibleForTesting
    375     protected PinnedSliceState createPinnedSlice(Uri uri, String pkg) {
    376         return new PinnedSliceState(this, uri, pkg);
    377     }
    378 
    379     public Object getLock() {
    380         return mLock;
    381     }
    382 
    383     public Context getContext() {
    384         return mContext;
    385     }
    386 
    387     public Handler getHandler() {
    388         return mHandler;
    389     }
    390 
    391     protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
    392         return checkSlicePermission(uri, pkg, uid, pid, null);
    393     }
    394 
    395     private String getProviderPkg(Uri uri, int user) {
    396         long ident = Binder.clearCallingIdentity();
    397         try {
    398             IBinder token = new Binder();
    399             IActivityManager activityManager = ActivityManager.getService();
    400             ContentProviderHolder holder = null;
    401             String providerName = getUriWithoutUserId(uri).getAuthority();
    402             try {
    403                 try {
    404                     holder = activityManager.getContentProviderExternal(
    405                             providerName, getUserIdFromUri(uri, user), token);
    406                     if (holder != null && holder.info != null) {
    407                         return holder.info.packageName;
    408                     } else {
    409                         return null;
    410                     }
    411                 } finally {
    412                     if (holder != null && holder.provider != null) {
    413                         activityManager.removeContentProviderExternal(providerName, token);
    414                     }
    415                 }
    416             } catch (RemoteException e) {
    417                 // Can't happen.
    418                 throw e.rethrowAsRuntimeException();
    419             }
    420         } finally {
    421             // I know, the double finally seems ugly, but seems safest for the identity.
    422             Binder.restoreCallingIdentity(ident);
    423         }
    424     }
    425 
    426     private void enforceCrossUser(String pkg, Uri uri) {
    427         int user = Binder.getCallingUserHandle().getIdentifier();
    428         if (getUserIdFromUri(uri, user) != user) {
    429             getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
    430                     "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
    431         }
    432     }
    433 
    434     private void enforceAccess(String pkg, Uri uri) throws RemoteException {
    435         if (checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingPid())
    436                 != PERMISSION_GRANTED) {
    437             int userId = ContentProvider.getUserIdFromUri(uri,
    438                     Binder.getCallingUserHandle().getIdentifier());
    439             if (!Objects.equals(pkg, getProviderPkg(uri, userId))) {
    440                 throw new SecurityException("Access to slice " + uri + " is required");
    441             }
    442         }
    443         enforceCrossUser(pkg, uri);
    444     }
    445 
    446     private void verifyCaller(String pkg) {
    447         mAppOps.checkPackage(Binder.getCallingUid(), pkg);
    448     }
    449 
    450     private boolean hasFullSliceAccess(String pkg, int userId) {
    451         long ident = Binder.clearCallingIdentity();
    452         try {
    453             boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
    454                     || isGrantedFullAccess(pkg, userId);
    455             return ret;
    456         } finally {
    457             Binder.restoreCallingIdentity(ident);
    458         }
    459     }
    460 
    461     private boolean isAssistant(String pkg, int userId) {
    462         return getAssistantMatcher(userId).matches(pkg);
    463     }
    464 
    465     private boolean isDefaultHomeApp(String pkg, int userId) {
    466         return getHomeMatcher(userId).matches(pkg);
    467     }
    468 
    469     private PackageMatchingCache getAssistantMatcher(int userId) {
    470         PackageMatchingCache matcher = mAssistantLookup.get(userId);
    471         if (matcher == null) {
    472             matcher = new PackageMatchingCache(() -> getAssistant(userId));
    473             mAssistantLookup.put(userId, matcher);
    474         }
    475         return matcher;
    476     }
    477 
    478     private PackageMatchingCache getHomeMatcher(int userId) {
    479         PackageMatchingCache matcher = mHomeLookup.get(userId);
    480         if (matcher == null) {
    481             matcher = new PackageMatchingCache(() -> getDefaultHome(userId));
    482             mHomeLookup.put(userId, matcher);
    483         }
    484         return matcher;
    485     }
    486 
    487     private String getAssistant(int userId) {
    488         final ComponentName cn = mAssistUtils.getAssistComponentForUser(userId);
    489         if (cn == null) {
    490             return null;
    491         }
    492         return cn.getPackageName();
    493     }
    494 
    495     // Based on getDefaultHome in ShortcutService.
    496     // TODO: Unify if possible
    497     @VisibleForTesting
    498     protected String getDefaultHome(int userId) {
    499         final long token = Binder.clearCallingIdentity();
    500         try {
    501             final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
    502 
    503             // Default launcher from package manager.
    504             final ComponentName defaultLauncher = mPackageManagerInternal
    505                     .getHomeActivitiesAsUser(allHomeCandidates, userId);
    506 
    507             ComponentName detected = null;
    508             if (defaultLauncher != null) {
    509                 detected = defaultLauncher;
    510             }
    511 
    512             if (detected == null) {
    513                 // If we reach here, that means it's the first check since the user was created,
    514                 // and there's already multiple launchers and there's no default set.
    515                 // Find the system one with the highest priority.
    516                 // (We need to check the priority too because of FallbackHome in Settings.)
    517                 // If there's no system launcher yet, then no one can access slices, until
    518                 // the user explicitly sets one.
    519                 final int size = allHomeCandidates.size();
    520 
    521                 int lastPriority = Integer.MIN_VALUE;
    522                 for (int i = 0; i < size; i++) {
    523                     final ResolveInfo ri = allHomeCandidates.get(i);
    524                     if (!ri.activityInfo.applicationInfo.isSystemApp()) {
    525                         continue;
    526                     }
    527                     if (ri.priority < lastPriority) {
    528                         continue;
    529                     }
    530                     detected = ri.activityInfo.getComponentName();
    531                     lastPriority = ri.priority;
    532                 }
    533             }
    534             return detected != null ? detected.getPackageName() : null;
    535         } finally {
    536             Binder.restoreCallingIdentity(token);
    537         }
    538     }
    539 
    540     private boolean isGrantedFullAccess(String pkg, int userId) {
    541         return mPermissions.hasFullAccess(pkg, userId);
    542     }
    543 
    544     private static ServiceThread createHandler() {
    545         ServiceThread handlerThread = new ServiceThread(TAG,
    546                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
    547         handlerThread.start();
    548         return handlerThread;
    549     }
    550 
    551     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    552         @Override
    553         public void onReceive(Context context, Intent intent) {
    554             final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
    555             if (userId == UserHandle.USER_NULL) {
    556                 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
    557                 return;
    558             }
    559             Uri data = intent.getData();
    560             String pkg = data != null ? data.getSchemeSpecificPart() : null;
    561             if (pkg == null) {
    562                 Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
    563                 return;
    564             }
    565             switch (intent.getAction()) {
    566                 case Intent.ACTION_PACKAGE_REMOVED:
    567                     final boolean replacing =
    568                             intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
    569                     if (!replacing) {
    570                         mPermissions.removePkg(pkg, userId);
    571                     }
    572                     break;
    573                 case Intent.ACTION_PACKAGE_DATA_CLEARED:
    574                     mPermissions.removePkg(pkg, userId);
    575                     break;
    576             }
    577         }
    578     };
    579 
    580     public String[] getAllPackagesGranted(String authority) {
    581         String pkg = getProviderPkg(new Uri.Builder()
    582                 .scheme(ContentResolver.SCHEME_CONTENT)
    583                 .authority(authority)
    584                 .build(), 0);
    585         return mPermissions.getAllPackagesGranted(pkg);
    586     }
    587 
    588     /**
    589      * Holder that caches a package that has access to a slice.
    590      */
    591     static class PackageMatchingCache {
    592 
    593         private final Supplier<String> mPkgSource;
    594         private String mCurrentPkg;
    595 
    596         public PackageMatchingCache(Supplier<String> pkgSource) {
    597             mPkgSource = pkgSource;
    598         }
    599 
    600         public boolean matches(String pkgCandidate) {
    601             if (pkgCandidate == null) return false;
    602 
    603             if (Objects.equals(pkgCandidate, mCurrentPkg)) {
    604                 return true;
    605             }
    606             // Failed on cached value, try updating.
    607             mCurrentPkg = mPkgSource.get();
    608             return Objects.equals(pkgCandidate, mCurrentPkg);
    609         }
    610     }
    611 
    612     public static class Lifecycle extends SystemService {
    613         private SliceManagerService mService;
    614 
    615         public Lifecycle(Context context) {
    616             super(context);
    617         }
    618 
    619         @Override
    620         public void onStart() {
    621             mService = new SliceManagerService(getContext());
    622             publishBinderService(Context.SLICE_SERVICE, mService);
    623         }
    624 
    625         @Override
    626         public void onBootPhase(int phase) {
    627             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
    628                 mService.systemReady();
    629             }
    630         }
    631 
    632         @Override
    633         public void onUnlockUser(int userHandle) {
    634             mService.onUnlockUser(userHandle);
    635         }
    636 
    637         @Override
    638         public void onStopUser(int userHandle) {
    639             mService.onStopUser(userHandle);
    640         }
    641     }
    642 
    643     private class SliceGrant {
    644         private final Uri mUri;
    645         private final String mPkg;
    646         private final int mUserId;
    647 
    648         public SliceGrant(Uri uri, String pkg, int userId) {
    649             mUri = uri;
    650             mPkg = pkg;
    651             mUserId = userId;
    652         }
    653 
    654         @Override
    655         public int hashCode() {
    656             return mUri.hashCode() + mPkg.hashCode();
    657         }
    658 
    659         @Override
    660         public boolean equals(Object obj) {
    661             if (!(obj instanceof SliceGrant)) return false;
    662             SliceGrant other = (SliceGrant) obj;
    663             return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg)
    664                     && (other.mUserId == mUserId);
    665         }
    666     }
    667 }
    668